diff --git a/crates/buttplug_client/Cargo.toml b/crates/buttplug_client/Cargo.toml index 1aed14600..b47b9aa69 100644 --- a/crates/buttplug_client/Cargo.toml +++ b/crates/buttplug_client/Cargo.toml @@ -19,14 +19,13 @@ doctest = true doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } futures = "0.3.31" thiserror = "2.0.18" log = "0.4.29" getset = "0.1.6" tokio = { version = "1.49.0", features = ["macros"] } dashmap = { version = "6.1.0" } -tracing-futures = "0.2.5" tracing = "0.1.44" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" diff --git a/crates/buttplug_client/src/device/feature.rs b/crates/buttplug_client/src/device/feature.rs index d81fd5605..0b9047f21 100644 --- a/crates/buttplug_client/src/device/feature.rs +++ b/crates/buttplug_client/src/device/feature.rs @@ -37,7 +37,7 @@ use crate::{ device::ClientDeviceCommandValue, }; -#[derive(Getters, CopyGetters, Clone)] +#[derive(Getters, CopyGetters, Clone, Debug)] pub struct ClientDeviceFeature { #[getset(get_copy = "pub")] device_index: u32, @@ -76,7 +76,7 @@ impl ClientDeviceFeature { ClientDeviceCommandValue::Percent(f) => self.convert_float_value(feature_output, *f)?, ClientDeviceCommandValue::Steps(i) => *i, }; - if feature_output.step_limit().contains(&value) { + if feature_output.step_limit().contains(value) { Ok(value) } else { Err(ButtplugClientError::ButtplugOutputCommandConversionError( @@ -182,7 +182,7 @@ impl ClientDeviceFeature { pub fn run_input_subscribe(&self, sensor_type: InputType) -> ButtplugClientResultFuture { if let Some(sensor_map) = self.feature.input() && let Some(sensor) = sensor_map.get(sensor_type) - && sensor.command().contains(&InputCommandType::Subscribe) + && sensor.command().contains(InputCommandType::Subscribe) { let msg = InputCmdV4::new( self.device_index, @@ -202,7 +202,7 @@ impl ClientDeviceFeature { pub fn run_input_unsubscribe(&self, sensor_type: InputType) -> ButtplugClientResultFuture { if let Some(sensor_map) = self.feature.input() && let Some(sensor) = sensor_map.get(sensor_type) - && sensor.command().contains(&InputCommandType::Subscribe) + && sensor.command().contains(InputCommandType::Subscribe) { let msg = InputCmdV4::new( self.device_index, @@ -222,7 +222,7 @@ impl ClientDeviceFeature { pub fn run_input_read(&self, sensor_type: InputType) -> ButtplugClientResultFuture { if let Some(sensor_map) = self.feature.input() && let Some(sensor) = sensor_map.get(sensor_type) - && sensor.command().contains(&InputCommandType::Read) + && sensor.command().contains(InputCommandType::Read) { let msg = InputCmdV4::new( self.device_index, diff --git a/crates/buttplug_client/src/lib.rs b/crates/buttplug_client/src/lib.rs index c0ad8415d..664461184 100644 --- a/crates/buttplug_client/src/lib.rs +++ b/crates/buttplug_client/src/lib.rs @@ -20,7 +20,17 @@ use buttplug_core::{ connector::{ButtplugConnector, ButtplugConnectorError}, errors::{ButtplugError, ButtplugHandshakeError}, message::{ - BUTTPLUG_CURRENT_API_MAJOR_VERSION, BUTTPLUG_CURRENT_API_MINOR_VERSION, ButtplugClientMessageV4, ButtplugServerMessageV4, InputType, PingV0, RequestDeviceListV0, RequestServerInfoV4, StartScanningV0, StopCmdV4, StopScanningV0 + BUTTPLUG_CURRENT_API_MAJOR_VERSION, + BUTTPLUG_CURRENT_API_MINOR_VERSION, + ButtplugClientMessageV4, + ButtplugServerMessageV4, + InputType, + PingV0, + RequestDeviceListV0, + RequestServerInfoV4, + StartScanningV0, + StopCmdV4, + StopScanningV0, }, util::{async_manager, stream::convert_broadcast_receiver_to_stream}, }; @@ -43,7 +53,7 @@ use std::{ use strum_macros::Display; use thiserror::Error; use tokio::sync::{Mutex, broadcast, mpsc}; -use tracing_futures::Instrument; +use tracing::info_span; /// Result type used for public APIs. /// @@ -103,7 +113,7 @@ pub enum ButtplugClientError { /// Error converting output command: {} ButtplugOutputCommandConversionError(String), /// Multiple inputs available for {}, must use specific feature - ButtplugMultipleInputAvailableError(InputType) + ButtplugMultipleInputAvailableError(InputType), } /// Enum representing different events that can be emitted by a client. @@ -286,14 +296,11 @@ impl ButtplugClient { // Take the request receiver - if None, a previous connection consumed it and we can't reconnect // without creating a new client (the sender is tied to this receiver) - let request_receiver = self - .request_receiver - .lock() - .await - .take() - .ok_or(ButtplugConnectorError::ConnectorGenericError( + let request_receiver = self.request_receiver.lock().await.take().ok_or( + ButtplugConnectorError::ConnectorGenericError( "Cannot reconnect - request channel already consumed. Create a new client.".to_string(), - ))?; + ), + )?; info!("Connecting to server."); let (connector_sender, connector_receiver) = mpsc::channel(256); @@ -316,8 +323,8 @@ impl ButtplugClient { async_manager::spawn( async move { client_event_loop.run().await; - } - .instrument(tracing::info_span!("Client Loop Span")), + }, + info_span!("ClientLoop").or_current(), ); self.run_handshake().await } diff --git a/crates/buttplug_client_in_process/Cargo.toml b/crates/buttplug_client_in_process/Cargo.toml index 98d5a546a..ccd393638 100644 --- a/crates/buttplug_client_in_process/Cargo.toml +++ b/crates/buttplug_client_in_process/Cargo.toml @@ -30,17 +30,17 @@ websocket-manager=["buttplug_server_hwmgr_websocket"] xinput-manager=["buttplug_server_hwmgr_xinput"] [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_client = { version = "10.0.0", path = "../buttplug_client" } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } -buttplug_server_hwmgr_btleplug = { version = "10.0.0", path = "../buttplug_server_hwmgr_btleplug", optional = true} -buttplug_server_hwmgr_hid = { version = "10.0.0", path = "../buttplug_server_hwmgr_hid", optional = true} -buttplug_server_hwmgr_lovense_connect = { version = "10.0.0", path = "../buttplug_server_hwmgr_lovense_connect", optional = true} -buttplug_server_hwmgr_lovense_dongle = { version = "10.0.0", path = "../buttplug_server_hwmgr_lovense_dongle", optional = true} -buttplug_server_hwmgr_serial = { version = "10.0.0", path = "../buttplug_server_hwmgr_serial", optional = true} -buttplug_server_hwmgr_websocket = { version = "10.0.0", path = "../buttplug_server_hwmgr_websocket", optional = true} -buttplug_server_hwmgr_xinput = { version = "10.0.0", path = "../buttplug_server_hwmgr_xinput", optional = true} +buttplug_server_hwmgr_btleplug = { version = "10.0.0", path = "../buttplug_server_hwmgr_btleplug", optional = true } +buttplug_server_hwmgr_hid = { version = "10.0.0", path = "../buttplug_server_hwmgr_hid", optional = true } +buttplug_server_hwmgr_lovense_connect = { version = "10.0.0", path = "../buttplug_server_hwmgr_lovense_connect", optional = true } +buttplug_server_hwmgr_lovense_dongle = { version = "10.0.0", path = "../buttplug_server_hwmgr_lovense_dongle", optional = true } +buttplug_server_hwmgr_serial = { version = "10.0.0", path = "../buttplug_server_hwmgr_serial", optional = true } +buttplug_server_hwmgr_websocket = { version = "10.0.0", path = "../buttplug_server_hwmgr_websocket", optional = true } +buttplug_server_hwmgr_xinput = { version = "10.0.0", path = "../buttplug_server_hwmgr_xinput", optional = true } futures = "0.3.31" futures-util = "0.3.31" thiserror = "2.0.18" @@ -48,5 +48,4 @@ log = "0.4.29" getset = "0.1.6" tokio = { version = "1.49.0", features = ["macros"] } dashmap = { version = "6.1.0" } -tracing-futures = "0.2.5" tracing = "0.1.44" diff --git a/crates/buttplug_client_in_process/src/in_process_connector.rs b/crates/buttplug_client_in_process/src/in_process_connector.rs index 95ca314f5..b0f0ce577 100644 --- a/crates/buttplug_client_in_process/src/in_process_connector.rs +++ b/crates/buttplug_client_in_process/src/in_process_connector.rs @@ -29,7 +29,7 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, }; use tokio::sync::mpsc::{Sender, channel}; -use tracing_futures::Instrument; +use tracing::info_span; #[derive(Default)] pub struct ButtplugInProcessClientConnectorBuilder { @@ -116,21 +116,24 @@ impl ButtplugConnector self.server_outbound_sender = message_sender; let server_recv = self.server.server_version_event_stream(); async move { - async_manager::spawn(async move { - info!("Starting In Process Client Connector Event Sender Loop"); - pin_mut!(server_recv); - while let Some(event) = server_recv.next().await { - // If we get an error back, it means the client dropped our event - // handler, so just stop trying. Otherwise, since this is an - // in-process conversion, we can unwrap because we know our - // try_into() will always succeed (which may not be the case with - // remote connections that have different spec versions). - if send.send(event).await.is_err() { - break; + async_manager::spawn( + async move { + info!("Starting In Process Client Connector Event Sender Loop"); + pin_mut!(server_recv); + while let Some(event) = server_recv.next().await { + // If we get an error back, it means the client dropped our event + // handler, so just stop trying. Otherwise, since this is an + // in-process conversion, we can unwrap because we know our + // try_into() will always succeed (which may not be the case with + // remote connections that have different spec versions). + if send.send(event).await.is_err() { + break; + } } - } - info!("Stopping In Process Client Connector Event Sender Loop, due to channel receiver being dropped."); - }.instrument(tracing::info_span!("InProcessClientConnectorEventSenderLoop"))); + info!("Stopping In Process Client Connector Event Sender Loop, due to channel receiver being dropped."); + }, + info_span!("InProcessClientConnectorEventSenderLoop").or_current() + ); connected.store(true, Ordering::Relaxed); Ok(()) }.boxed() diff --git a/crates/buttplug_core/Cargo.toml b/crates/buttplug_core/Cargo.toml index 17671948a..8c02c0109 100644 --- a/crates/buttplug_core/Cargo.toml +++ b/crates/buttplug_core/Cargo.toml @@ -20,7 +20,7 @@ doc = true [features] default=["tokio-runtime"] -tokio-runtime=["tokio/rt"] +tokio-runtime=["tokio/rt", "tokio/time"] wasm=[] # Only build docs on one platform (linux) @@ -46,9 +46,12 @@ log = "0.4.29" getset = "0.1.6" jsonschema = { version = "0.38.1", default-features = false } cfg-if = "1.0.4" -tokio = { version = "1.49.0", features = ["sync", "time", "macros"] } +tokio = { version = "1.49.0", features = ["sync", "macros"] } async-stream = "0.3.6" strum_macros = "0.27.2" strum = "0.27.2" derive_builder = "0.20.2" enum_dispatch = "0.3" +async-trait = "0.1.89" +tracing = "0.1.44" +enumflags2 = "0.7.12" diff --git a/crates/buttplug_core/src/connector/remote_connector.rs b/crates/buttplug_core/src/connector/remote_connector.rs index dc328534a..984cfa93f 100644 --- a/crates/buttplug_core/src/connector/remote_connector.rs +++ b/crates/buttplug_core/src/connector/remote_connector.rs @@ -24,6 +24,7 @@ use futures::{FutureExt, future::BoxFuture, select}; use log::*; use std::marker::PhantomData; use tokio::sync::mpsc::{Receiver, Sender, channel}; +use tracing::info_span; enum ButtplugRemoteConnectorMessage where @@ -234,21 +235,24 @@ where // If we connect successfully, we get back the channel from the transport // to send outgoing messages and receieve incoming events, all serialized. Ok(()) => { - async_manager::spawn(async move { - remote_connector_event_loop::< - TransportType, - SerializerType, - OutboundMessageType, - InboundMessageType, - >( - connector_outgoing_receiver, - connector_incoming_sender, - transport, - transport_outgoing_sender, - transport_incoming_receiver, - ) - .await - }); + async_manager::spawn( + async move { + remote_connector_event_loop::< + TransportType, + SerializerType, + OutboundMessageType, + InboundMessageType, + >( + connector_outgoing_receiver, + connector_incoming_sender, + transport, + transport_outgoing_sender, + transport_incoming_receiver, + ) + .await + }, + info_span!("ButtplugRemoteConnectorEventLoop").or_current(), + ); Ok(()) } Err(e) => Err(e), diff --git a/crates/buttplug_core/src/connector/transport/mod.rs b/crates/buttplug_core/src/connector/transport/mod.rs index e0d2ea44e..2d534d022 100644 --- a/crates/buttplug_core/src/connector/transport/mod.rs +++ b/crates/buttplug_core/src/connector/transport/mod.rs @@ -12,7 +12,7 @@ use crate::connector::{ ButtplugConnectorResultFuture, ButtplugSerializedMessage, }; -use displaydoc::Display; +use strum_macros::Display; use futures::future::BoxFuture; use thiserror::Error; use tokio::sync::mpsc::{Receiver, Sender}; diff --git a/crates/buttplug_core/src/connector/transport/stream.rs b/crates/buttplug_core/src/connector/transport/stream.rs index 8e6676e4f..d4a81c1ec 100644 --- a/crates/buttplug_core/src/connector/transport/stream.rs +++ b/crates/buttplug_core/src/connector/transport/stream.rs @@ -21,6 +21,7 @@ use futures::{ FutureExt, future::{self, BoxFuture}, }; +use tracing::info_span; use std::sync::Arc; use tokio::{ @@ -88,7 +89,7 @@ impl ButtplugConnectorTransport for ButtplugStreamTransport { } } } - }); + }, info_span!("ButtplugStreamTransportEventLoop").or_current()); Ok(()) }.boxed() } diff --git a/crates/buttplug_core/src/errors.rs b/crates/buttplug_core/src/errors.rs index 3c03bce4f..740d48bc1 100644 --- a/crates/buttplug_core/src/errors.rs +++ b/crates/buttplug_core/src/errors.rs @@ -15,6 +15,7 @@ use super::message::{ OutputType, serializer::ButtplugSerializerError, }; +use displaydoc::Display; use futures::future::BoxFuture; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -51,7 +52,7 @@ impl_error_to_future!( /// a remote network connection cannot be established), see /// [crate::connector::ButtplugConnectorError]. -#[derive(Debug, Error, Display, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Display, Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ButtplugHandshakeError { /// Expected either a ServerInfo or Error message, received {0} UnexpectedHandshakeMessageReceived(String), @@ -130,7 +131,7 @@ pub enum ButtplugDeviceError { DeviceCommunicationError(String), /// Device feature only has {0} steps for control, but {1} steps specified. DeviceStepRangeError(i32, i32), - /// Device got {} output command but has no viable outputs + /// Device got {0} output command but has no viable outputs DeviceNoOutputError(OutputType), /// Device got {0} input command but has no viable inputs DeviceNoInputError(InputType), diff --git a/crates/buttplug_core/src/lib.rs b/crates/buttplug_core/src/lib.rs index 602bcf121..46bec0134 100644 --- a/crates/buttplug_core/src/lib.rs +++ b/crates/buttplug_core/src/lib.rs @@ -12,9 +12,6 @@ pub mod errors; pub mod message; pub mod util; -#[macro_use] -extern crate strum_macros; - use errors::ButtplugError; use futures::future::{self, BoxFuture, FutureExt}; diff --git a/crates/buttplug_core/src/message/device_feature.rs b/crates/buttplug_core/src/message/device_feature.rs index 94658f45b..de3ab2596 100644 --- a/crates/buttplug_core/src/message/device_feature.rs +++ b/crates/buttplug_core/src/message/device_feature.rs @@ -5,11 +5,12 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use crate::{message::InputCommandType, util::range_serialize::*}; +use crate::{message::InputCommandTypeFlags, util::range::RangeInclusive}; use derive_builder::Builder; use getset::{CopyGetters, Getters, MutGetters, Setters}; -use serde::{Deserialize, Serialize, Serializer, ser::SerializeSeq}; -use std::{collections::HashSet, hash::Hash, ops::RangeInclusive}; +use serde::{Deserialize, Serialize}; +use std::hash::Hash; +use strum_macros::{Display, EnumIter, EnumString}; #[derive( Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, EnumIter, EnumString, @@ -107,20 +108,6 @@ impl DeviceFeature { } } -fn range_sequence_serialize( - range_vec: &Vec>, - serializer: S, -) -> Result -where - S: Serializer, -{ - let mut seq = serializer.serialize_seq(Some(range_vec.len()))?; - for range in range_vec { - seq.serialize_element(&vec![*range.start(), *range.end()])?; - } - seq.end() -} - pub trait DeviceFeatureOutputLimits { fn step_count(&self) -> u32; fn step_limit(&self) -> RangeInclusive; @@ -130,7 +117,6 @@ pub trait DeviceFeatureOutputLimits { #[serde(rename_all = "PascalCase")] pub struct DeviceFeatureOutputValueProperties { #[getset(get = "pub")] - #[serde(serialize_with = "range_serialize")] value: RangeInclusive, } @@ -142,7 +128,7 @@ impl DeviceFeatureOutputValueProperties { } pub fn step_count(&self) -> u32 { - *self.value.end() as u32 + self.value.end() as u32 } } @@ -159,10 +145,8 @@ impl DeviceFeatureOutputLimits for DeviceFeatureOutputValueProperties { #[serde(rename_all = "PascalCase")] pub struct DeviceFeatureOutputHwPositionWithDurationProperties { #[getset(get = "pub")] - #[serde(serialize_with = "range_serialize")] value: RangeInclusive, #[getset(get = "pub")] - #[serde(serialize_with = "range_serialize")] duration: RangeInclusive, } @@ -175,7 +159,7 @@ impl DeviceFeatureOutputHwPositionWithDurationProperties { } pub fn step_count(&self) -> u32 { - *self.value.end() as u32 + self.value.end() as u32 } } @@ -258,17 +242,13 @@ impl DeviceFeatureOutput { #[serde(rename_all = "PascalCase")] pub struct DeviceFeatureInputProperties { #[getset(get = "pub", get_mut = "pub(super)")] - #[serde(serialize_with = "range_sequence_serialize")] value: Vec>, #[getset(get = "pub")] - command: HashSet, + command: InputCommandTypeFlags, } impl DeviceFeatureInputProperties { - pub fn new( - value: &Vec>, - sensor_commands: &HashSet, - ) -> Self { + pub fn new(value: &Vec>, sensor_commands: InputCommandTypeFlags) -> Self { Self { value: value.clone(), command: sensor_commands.clone(), diff --git a/crates/buttplug_core/src/message/mod.rs b/crates/buttplug_core/src/message/mod.rs index b11709c1e..394306b3c 100644 --- a/crates/buttplug_core/src/message/mod.rs +++ b/crates/buttplug_core/src/message/mod.rs @@ -28,6 +28,7 @@ use crate::errors::ButtplugMessageError; use enum_dispatch::enum_dispatch; use serde_repr::{Deserialize_repr, Serialize_repr}; use std::convert::TryFrom; +use strum_macros::Display; use super::errors::ButtplugError; diff --git a/crates/buttplug_core/src/message/v0/error.rs b/crates/buttplug_core/src/message/v0/error.rs index 717a81988..f589b9c6c 100644 --- a/crates/buttplug_core/src/message/v0/error.rs +++ b/crates/buttplug_core/src/message/v0/error.rs @@ -45,7 +45,7 @@ pub struct ErrorV0 { #[serde(rename = "ErrorMessage")] #[getset(get = "pub")] error_message: String, - #[serde(skip)] + #[serde(rename = "OriginalError", skip_serializing_if = "Option::is_none")] original_error: Option, } @@ -88,10 +88,6 @@ impl ErrorV0 { if let Some(ref original_error) = self.original_error { original_error.clone() } else { - // Try deserializing what's in the error_message field - if let Ok(deserialized_msg) = serde_json::from_str(&self.error_message) { - return deserialized_msg; - } ButtplugError::from(self.clone()) } } @@ -108,9 +104,7 @@ impl From for ErrorV0 { ButtplugError::ButtplugHandshakeError { .. } => ErrorCode::ErrorHandshake, ButtplugError::ButtplugUnknownError { .. } => ErrorCode::ErrorUnknown, }; - // SAFETY: ButtplugError derives Serialize and contains only serializable fields. - // Serialization failure would indicate a bug in the type definition, not a runtime condition. - let msg = serde_json::to_string(&error).expect("ButtplugError derives Serialize"); + let msg = error.to_string(); ErrorV0::new(code, &msg, Some(error)) } } diff --git a/crates/buttplug_core/src/message/v4/input_cmd.rs b/crates/buttplug_core/src/message/v4/input_cmd.rs index 1b5042354..ee1cacc53 100644 --- a/crates/buttplug_core/src/message/v4/input_cmd.rs +++ b/crates/buttplug_core/src/message/v4/input_cmd.rs @@ -5,6 +5,8 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. +use std::{fmt, ops::{Deref, DerefMut}}; + use crate::message::{ ButtplugDeviceMessage, ButtplugMessage, @@ -13,8 +15,12 @@ use crate::message::{ InputType, }; use getset::CopyGetters; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::{SeqAccess, Visitor}, ser::SerializeSeq}; +use enumflags2::{BitFlags, bitflags}; +use strum_macros::Display; +#[bitflags] +#[repr(u8)] #[derive(Debug, Display, PartialEq, Eq, Clone, Serialize, Deserialize, Hash, Copy)] pub enum InputCommandType { #[serde(alias = "read")] @@ -83,3 +89,85 @@ impl ButtplugMessageValidator for InputCmdV4 { // TODO Should expected_length always be > 0? } } + +#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)] +pub struct InputCommandTypeFlags(BitFlags); + +impl InputCommandTypeFlags { + pub fn new(flags: BitFlags) -> Self { + Self(flags) + } + + pub fn empty() -> Self { + Self(BitFlags::empty()) + } +} + +impl Deref for InputCommandTypeFlags { + type Target = BitFlags; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for InputCommandTypeFlags { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for InputCommandTypeFlags { + fn from(flags: BitFlags) -> Self { + Self(flags) + } +} + +impl From for BitFlags { + fn from(flags: InputCommandTypeFlags) -> Self { + flags.0 + } +} + +impl Serialize for InputCommandTypeFlags { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.0.len()))?; + for cmd in self.0.iter() { + seq.serialize_element(&cmd)?; + } + seq.end() + } +} + +impl<'de> Deserialize<'de> for InputCommandTypeFlags { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FlagsVisitor; + + impl<'de> Visitor<'de> for FlagsVisitor { + type Value = InputCommandTypeFlags; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an array of InputCommandType values") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut flags = BitFlags::empty(); + while let Some(cmd) = seq.next_element::()? { + flags |= cmd; + } + Ok(InputCommandTypeFlags(flags)) + } + } + + deserializer.deserialize_seq(FlagsVisitor) + } +} diff --git a/crates/buttplug_core/src/message/v4/input_reading.rs b/crates/buttplug_core/src/message/v4/input_reading.rs index 52f6665c5..1dd50d80c 100644 --- a/crates/buttplug_core/src/message/v4/input_reading.rs +++ b/crates/buttplug_core/src/message/v4/input_reading.rs @@ -8,6 +8,7 @@ use crate::message::{ButtplugDeviceMessage, ButtplugMessage, ButtplugMessageValidator, InputType}; use getset::{CopyGetters, Getters}; use serde::{Deserialize, Serialize}; +use strum_macros::Display; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, CopyGetters)] #[getset(get_copy = "pub")] diff --git a/crates/buttplug_core/src/message/v4/mod.rs b/crates/buttplug_core/src/message/v4/mod.rs index aa94b7336..4ebcb0c4c 100644 --- a/crates/buttplug_core/src/message/v4/mod.rs +++ b/crates/buttplug_core/src/message/v4/mod.rs @@ -18,7 +18,7 @@ mod stop_cmd; pub use { device_list::DeviceListV4, device_message_info::DeviceMessageInfoV4, - input_cmd::{InputCmdV4, InputCommandType}, + input_cmd::{InputCmdV4, InputCommandType, InputCommandTypeFlags}, input_reading::{InputReadingV4, InputTypeReading, InputValue}, output_cmd::{OutputCmdV4, OutputCommand, OutputHwPositionWithDuration, OutputValue}, request_server_info::RequestServerInfoV4, diff --git a/crates/buttplug_core/src/message/v4/output_cmd.rs b/crates/buttplug_core/src/message/v4/output_cmd.rs index d85ddaceb..719c42239 100644 --- a/crates/buttplug_core/src/message/v4/output_cmd.rs +++ b/crates/buttplug_core/src/message/v4/output_cmd.rs @@ -17,6 +17,7 @@ use crate::{ }; use getset::CopyGetters; use serde::{Deserialize, Serialize}; +use strum_macros::Display; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, CopyGetters)] #[getset(get_copy = "pub")] diff --git a/crates/buttplug_core/src/message/v4/spec_enums.rs b/crates/buttplug_core/src/message/v4/spec_enums.rs index f58f7aac9..6d80ed2f9 100644 --- a/crates/buttplug_core/src/message/v4/spec_enums.rs +++ b/crates/buttplug_core/src/message/v4/spec_enums.rs @@ -22,6 +22,7 @@ use crate::message::{ }; use enum_dispatch::enum_dispatch; use serde::{Deserialize, Serialize}; +use strum_macros::Display; use super::{DeviceListV4, InputReadingV4}; diff --git a/crates/buttplug_core/src/util/async_manager/dummy.rs b/crates/buttplug_core/src/util/async_manager/dummy.rs index 1368e110d..5a8a7df40 100644 --- a/crates/buttplug_core/src/util/async_manager/dummy.rs +++ b/crates/buttplug_core/src/util/async_manager/dummy.rs @@ -5,31 +5,29 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use futures::{ - future::{Future, RemoteHandle}, - task::{FutureObj, Spawn, SpawnError}, -}; +use async_trait::async_trait; +use futures::task::FutureObj; -#[derive(Default)] +#[derive(Default, Debug)] pub struct DummyAsyncManager {} -impl Spawn for DummyAsyncManager { - fn spawn_obj(&self, _: FutureObj<'static, ()>) -> Result<(), SpawnError> { - unimplemented!("Dummy executor can't actually spawn!") +#[async_trait] +impl super::AsyncManager for DummyAsyncManager { + fn spawn(&self, _future: FutureObj<'static, ()>, _span: tracing::Span) { + unimplemented!( + "No async runtime available. Please set a global async manager using set_global_async_manager or enable tokio-runtime or wasm feature" + ); } -} -pub fn spawn(_: Fut) -where - Fut: Future + Send + 'static, -{ - unimplemented!("Dummy executor can't actually spawn!") -} + async fn sleep(&self, _duration: std::time::Duration) { + unimplemented!( + "No async runtime available. Please set a global async manager using set_global_async_manager or enable tokio-runtime or wasm feature" + ); + } -pub fn spawn_with_handle(_: Fut) -> Result, SpawnError> -where - Fut: Future + Send + 'static, - Fut::Output: Send, -{ - unimplemented!("Dummy executor can't actually spawn!") + async fn sleep_until(&self, _deadline: std::time::Instant) { + unimplemented!( + "No async runtime available. Please set a global async manager using set_global_async_manager or enable tokio-runtime or wasm feature" + ); + } } diff --git a/crates/buttplug_core/src/util/async_manager/mod.rs b/crates/buttplug_core/src/util/async_manager/mod.rs index 38bd98496..25917e6b6 100644 --- a/crates/buttplug_core/src/util/async_manager/mod.rs +++ b/crates/buttplug_core/src/util/async_manager/mod.rs @@ -5,16 +5,78 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. +use std::sync::OnceLock; + +use async_trait::async_trait; +use futures::task::FutureObj; +use tracing::Span; + cfg_if::cfg_if! { if #[cfg(feature = "wasm")] { - mod wasm_bindgen; - pub use self::wasm_bindgen::{WasmBindgenAsyncManager as AsyncManager, spawn, spawn_with_handle}; + mod wasm; + use self::wasm::{WasmAsyncManager as DefaultAsyncManager}; } else if #[cfg(feature = "tokio-runtime")] { mod tokio; - pub use self::tokio::{TokioAsyncManager as AsyncManager, spawn, spawn_with_handle}; + use self::tokio::{TokioAsyncManager as DefaultAsyncManager}; } else { mod dummy; - pub use dummy::{DummyAsyncManager as AsyncManager, spawn, spawn_with_handle}; - //std::compile_error!("Please choose a runtime feature: tokio-runtime, wasm-bindgen-runtime, dummy-runtime"); + use dummy::{DummyAsyncManager as DefaultAsyncManager}; } } + +static GLOBAL_ASYNC_MANAGER: OnceLock> = OnceLock::new(); + +pub fn set_global_async_manager(manager: Box) { + log::info!("Setting global async manager to {:?}", manager); + GLOBAL_ASYNC_MANAGER + .set(manager) + .expect("Global async manager can only be set once."); +} + +fn get_global_async_manager() -> &'static Box { + GLOBAL_ASYNC_MANAGER.get_or_init(|| { + let default_manager = DefaultAsyncManager::default(); + log::info!( + "No global async manager set, using {:?} according to feature flag.", + default_manager + ); + Box::new(default_manager) + }) +} + +/// The `AsyncManager` is a trait that abstracts over the async runtime used by Buttplug. +/// It is similar to [futures::task::Spawn] but also includes sleep functions since they also depend on the used async runtime. +/// It also forces instumentation of tracing spans for all spawned tasks. +/// This usually does not need to be used in user code, but is public to allow users to implement their own async runtimes if needed. +#[async_trait] +pub trait AsyncManager: std::fmt::Debug + Send + Sync { + /// Spawns a future onto the async runtime. The future must be `Send` and `'static` since it may be spawned onto a different thread. + /// The span parameter should be used to instrument the future with a tracing span. + fn spawn(&self, future: FutureObj<'static, ()>, span: Span); + async fn sleep(&self, duration: std::time::Duration); + async fn sleep_until(&self, deadline: std::time::Instant); +} + +/// Spawns a future onto the global async manager. +pub fn spawn(future: F, span: Span) +where + F: Future + Send + 'static, +{ + let async_manager = get_global_async_manager(); + + async_manager.spawn(Box::new(future).into(), span); +} + +/// Sleeps for the specified duration using the global async manager. +pub async fn sleep(duration: std::time::Duration) { + let async_manager = get_global_async_manager(); + + async_manager.sleep(duration).await; +} + +/// Sleeps until the specified deadline using the global async manager. +pub async fn sleep_until(deadline: std::time::Instant) { + let async_manager = get_global_async_manager(); + + async_manager.sleep_until(deadline).await; +} diff --git a/crates/buttplug_core/src/util/async_manager/tokio.rs b/crates/buttplug_core/src/util/async_manager/tokio.rs index b52c14d0e..7903ea4a4 100644 --- a/crates/buttplug_core/src/util/async_manager/tokio.rs +++ b/crates/buttplug_core/src/util/async_manager/tokio.rs @@ -5,37 +5,24 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use futures::{ - future::{Future, RemoteHandle}, - task::{FutureObj, Spawn, SpawnError, SpawnExt}, -}; -use tokio; +use async_trait::async_trait; +use futures::task::FutureObj; +use tracing::{Instrument, Span}; -#[derive(Default)] +#[derive(Default, Debug)] pub struct TokioAsyncManager {} -impl Spawn for TokioAsyncManager { - fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { - tokio::spawn(future); - Ok(()) +#[async_trait] +impl super::AsyncManager for TokioAsyncManager { + fn spawn(&self, future: FutureObj<'static, ()>, span: Span) { + tokio::task::spawn(future.instrument(span)); } -} -pub fn spawn(future: Fut) -where - Fut: Future + Send + 'static, -{ - // SAFETY: TokioAsyncManager::spawn_obj always returns Ok(()). The Result type is only - // present to satisfy the Spawn trait interface. - TokioAsyncManager::default() - .spawn(future) - .expect("TokioAsyncManager::spawn_obj always returns Ok") -} + async fn sleep(&self, duration: std::time::Duration) { + tokio::time::sleep(duration).await; + } -pub fn spawn_with_handle(future: Fut) -> Result, SpawnError> -where - Fut: Future + Send + 'static, - Fut::Output: Send, -{ - TokioAsyncManager::default().spawn_with_handle(future) + async fn sleep_until(&self, deadline: std::time::Instant) { + tokio::time::sleep_until(deadline.into()).await; + } } diff --git a/crates/buttplug_core/src/util/async_manager/wasm.rs b/crates/buttplug_core/src/util/async_manager/wasm.rs new file mode 100644 index 000000000..e4934e5f4 --- /dev/null +++ b/crates/buttplug_core/src/util/async_manager/wasm.rs @@ -0,0 +1,30 @@ +// 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 async_trait::async_trait; +use futures::task::FutureObj; + +use wasm_bindgen_futures::spawn_local; +use wasm_timer::tokio::{sleep, sleep_until}; + +#[derive(Default, Debug)] +pub struct WasmAsyncManager {} + +#[async_trait] +impl super::AsyncManager for WasmAsyncManager { + fn spawn(&self, future: FutureObj<'static, ()>, _span: tracing::Span) { + spawn_local(future); + } + + async fn sleep(&self, duration: std::time::Duration) { + sleep(duration).await; + } + + async fn sleep_until(&self, deadline: std::time::Instant) { + sleep_until(deadline).await; + } +} diff --git a/crates/buttplug_core/src/util/async_manager/wasm_bindgen.rs b/crates/buttplug_core/src/util/async_manager/wasm_bindgen.rs deleted file mode 100644 index 2851e1099..000000000 --- a/crates/buttplug_core/src/util/async_manager/wasm_bindgen.rs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 futures::{ - future::{Future, RemoteHandle}, - task::{FutureObj, Spawn, SpawnError, SpawnExt}, -}; - -use wasm_bindgen_futures::spawn_local; - -#[derive(Default)] -pub struct WasmBindgenAsyncManager {} - -impl Spawn for WasmBindgenAsyncManager { - fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { - spawn_local(future); - Ok(()) - } -} - -pub fn spawn(future: Fut) -where - Fut: Future + 'static, -{ - spawn_local(future); -} - -pub fn spawn_with_handle(future: Fut) -> Result, SpawnError> -where - Fut: Future + Send + 'static, - Fut::Output: Send, -{ - WasmBindgenAsyncManager::default().spawn_with_handle(future) -} diff --git a/crates/buttplug_core/src/util/json.rs b/crates/buttplug_core/src/util/json.rs index 52bce0401..e1ddf8b95 100644 --- a/crates/buttplug_core/src/util/json.rs +++ b/crates/buttplug_core/src/util/json.rs @@ -39,14 +39,15 @@ impl JSONValidator { /// # Parameters /// /// - `json_str`: JSON string to validate. - pub fn validate(&self, json_str: &str) -> Result<(), ButtplugSerializerError> { - let check_value = serde_json::from_str(json_str).map_err(|err| { + pub fn validate(&self, json_str: &str) -> Result { + let value = serde_json::from_str(json_str).map_err(|err| { ButtplugSerializerError::JsonSerializerError(format!("Message: {json_str} - Error: {err:?}")) })?; - self.schema.validate(&check_value).map_err(|err| { + self.schema.validate(&value).map_err(|err| { ButtplugSerializerError::JsonSerializerError(format!( "Error during JSON Schema Validation: {err:?}" )) - }) + })?; + Ok(value) } } diff --git a/crates/buttplug_core/src/util/mod.rs b/crates/buttplug_core/src/util/mod.rs index a8f87820a..162dc4202 100644 --- a/crates/buttplug_core/src/util/mod.rs +++ b/crates/buttplug_core/src/util/mod.rs @@ -10,10 +10,5 @@ pub mod async_manager; pub mod json; -pub mod range_serialize; +pub mod range; pub mod stream; - -#[cfg(not(feature = "wasm"))] -pub use tokio::time::sleep; -#[cfg(feature = "wasm")] -pub use wasmtimer::tokio::sleep; diff --git a/crates/buttplug_core/src/util/range.rs b/crates/buttplug_core/src/util/range.rs new file mode 100644 index 000000000..83da2fc4d --- /dev/null +++ b/crates/buttplug_core/src/util/range.rs @@ -0,0 +1,48 @@ +// 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 std::fmt; + +use serde::{Deserialize, Serialize}; + +/// A range bounded inclusively below and above (`start..=end`). +/// Use this instead of `std::ops::RangeInclusive` when directly iterating over the range is not required. +/// It uses less memory and doesn't need special serialization. +#[derive(Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct RangeInclusive([T; 2]); + +impl RangeInclusive { + pub fn new(start: T, end: T) -> Self { + Self([start, end]) + } + + pub fn start(&self) -> T { + self.0[0] + } + + pub fn end(&self) -> T { + self.0[1] + } + + pub fn is_empty(&self) -> bool { + self.0[0] > self.0[1] + } + + pub fn contains(&self, value: T) -> bool { + value >= self.0[0] && value <= self.0[1] + } +} + +impl fmt::Debug for RangeInclusive +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{:?}..={:?}]", self.0[0], self.0[1]) + } +} diff --git a/crates/buttplug_server/Cargo.toml b/crates/buttplug_server/Cargo.toml index f8c153131..3af56aaa6 100644 --- a/crates/buttplug_server/Cargo.toml +++ b/crates/buttplug_server/Cargo.toml @@ -24,7 +24,7 @@ default=[] wasm=["uuid/js"] [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" futures-util = "0.3.31" @@ -33,12 +33,10 @@ log = "0.4.29" getset = "0.1.6" tokio = { version = "1.49.0", features = ["macros"] } dashmap = { version = "6.1.0" } -tracing-futures = "0.2.5" tracing = "0.1.44" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" jsonschema = { version = "0.38.1", default-features = false } -once_cell = "1.21.3" tokio-stream = "0.1.18" strum_macros = "0.27.2" strum = "0.27.2" @@ -58,6 +56,8 @@ byteorder = "1.5.0" rand = { version = "0.8" } derive_more = { version = "2.1.1", features = ["from"] } evalexpr = { version = "13.1.0", features = ["rand"] } +litemap = "0.8.1" +compact_str = { version = "0.9.0", features = ["serde"] } [target.wasm32-unknown-unknown.dependencies] getrandom = { version = "0.3.4", features = ["wasm_js"]} diff --git a/crates/buttplug_server/src/device/device_handle.rs b/crates/buttplug_server/src/device/device_handle.rs index e2f711fbc..20cd0be87 100644 --- a/crates/buttplug_server/src/device/device_handle.rs +++ b/crates/buttplug_server/src/device/device_handle.rs @@ -16,18 +16,34 @@ use buttplug_core::{ ButtplugResultFuture, errors::{ButtplugDeviceError, ButtplugError}, message::{ - self, ButtplugMessage, ButtplugServerMessageV4, DeviceFeature, DeviceMessageInfoV4, - InputCommandType, InputType, OutputType, OutputValue, StopCmdV4, + self, + ButtplugMessage, + ButtplugServerMessageV4, + DeviceFeature, + DeviceMessageInfoV4, + InputCommandType, + InputType, + OutputCommand, + OutputType, + OutputValue, + StopCmdV4, }, util::{async_manager, stream::convert_broadcast_receiver_to_stream}, }; use buttplug_server_device_config::{ - DeviceConfigurationManager, ServerDeviceDefinition, UserDeviceIdentifier, + DeviceConfigurationManager, + ServerDeviceDefinition, + UserDeviceIdentifier, }; +use compact_str::CompactString; use dashmap::DashMap; use futures::future::{self, BoxFuture, FutureExt}; -use tokio::sync::{mpsc::{channel, Sender}, oneshot}; +use tokio::sync::{ + mpsc::{Sender, channel}, + oneshot, +}; use tokio_stream::StreamExt; +use tracing::info_span; use uuid::Uuid; use crate::{ @@ -43,7 +59,7 @@ use crate::{ use super::{ InternalDeviceEvent, - device_task::{spawn_device_task, DeviceTaskConfig}, + device_task::{DeviceTaskConfig, spawn_device_task}, hardware::{Hardware, HardwareCommand, HardwareConnector, HardwareEvent}, protocol::{ProtocolHandler, ProtocolKeepaliveStrategy, ProtocolSpecializer}, }; @@ -125,8 +141,8 @@ impl DeviceHandle { } /// Get the device's name - pub fn name(&self) -> String { - self.definition.name().to_owned() + pub fn name(&self) -> CompactString { + self.definition.name().clone() } /// Get the device's definition (contains features, display name, etc.) @@ -144,7 +160,11 @@ impl DeviceHandle { DeviceMessageInfoV4::new( index, &self.name(), - self.definition.display_name(), + &self + .definition + .display_name() + .as_ref() + .map(|n| n.to_string()), 100, &self .definition @@ -219,10 +239,14 @@ impl DeviceHandle { }); let identifier = self.identifier.clone(); - let handler_mapped_stream = self.handler.event_stream().map(move |incoming_message| { - let id = identifier.clone(); - DeviceEvent::Notification(id, incoming_message) - }); + let handler_mapped_stream = self + .handler + .clone() + .event_stream() + .map(move |incoming_message| { + let id = identifier.clone(); + DeviceEvent::Notification(id, incoming_message) + }); hardware_stream.merge(handler_mapped_stream) } @@ -238,7 +262,80 @@ impl DeviceHandle { self .last_output_command .insert(msg.feature_id(), msg.clone()); - self.handle_generic_command_result(self.handler.handle_output_cmd(msg)) + let result = self + .handler + .handle_output_cmd(msg) + .unwrap_or_else(|| self.handle_outputcmd_v4_default(msg)); + self.handle_generic_command_result(result) + } + + fn handle_outputcmd_v4_default( + &self, + cmd: &CheckedOutputCmdV4, + ) -> Result, ButtplugDeviceError> { + let output_command = cmd.output_command(); + match output_command { + OutputCommand::Constrict(x) => self.handler.handle_output_constrict_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value() + .try_into() + .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, + ), + OutputCommand::Spray(x) => self.handler.handle_output_spray_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value() + .try_into() + .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, + ), + OutputCommand::Oscillate(x) => self.handler.handle_output_oscillate_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value() + .try_into() + .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, + ), + OutputCommand::Rotate(x) => { + self + .handler + .handle_output_rotate_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) + } + OutputCommand::Vibrate(x) => self.handler.handle_output_vibrate_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value() + .try_into() + .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, + ), + OutputCommand::Position(x) => self.handler.handle_output_position_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value() + .try_into() + .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, + ), + OutputCommand::Temperature(x) => { + self + .handler + .handle_output_temperature_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) + } + OutputCommand::Led(x) => self.handler.handle_output_led_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value() + .try_into() + .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, + ), + OutputCommand::HwPositionWithDuration(x) => { + self.handler.handle_hw_position_with_duration_cmd( + cmd.feature_index(), + cmd.feature_id(), + x.value(), + x.duration(), + ) + } + } } fn handle_hardware_commands(&self, commands: Vec) -> ButtplugServerResultFuture { @@ -554,9 +651,7 @@ pub(super) async fn build_device_handle( strategy, ProtocolKeepaliveStrategy::RepeatLastPacketStrategyWithTiming(_) )) - && let Err(e) = device_handle - .stop(&StopCmdV4::default()) - .await + && let Err(e) = device_handle.stop(&StopCmdV4::default()).await { return Err(ButtplugDeviceError::DeviceConnectionError(format!( "Error setting up keepalive: {e}" @@ -568,44 +663,47 @@ pub(super) async fn build_device_handle( // to the device manager event loop via the provided sender. let event_stream = device_handle.event_stream(); let identifier = device_handle.identifier().clone(); - async_manager::spawn(async move { - futures::pin_mut!(event_stream); - loop { - let event = futures::StreamExt::next(&mut event_stream).await; - match event { - Some(DeviceEvent::Disconnected(id)) => { - if device_event_sender - .send(InternalDeviceEvent::Disconnected(id)) - .await - .is_err() - { - info!( - "Device event sender closed for device {:?}, stopping event forwarding.", - identifier - ); - break; + async_manager::spawn( + async move { + futures::pin_mut!(event_stream); + loop { + let event = futures::StreamExt::next(&mut event_stream).await; + match event { + Some(DeviceEvent::Disconnected(id)) => { + if device_event_sender + .send(InternalDeviceEvent::Disconnected(id)) + .await + .is_err() + { + info!( + "Device event sender closed for device {:?}, stopping event forwarding.", + identifier + ); + break; + } } - } - Some(DeviceEvent::Notification(id, msg)) => { - if device_event_sender - .send(InternalDeviceEvent::Notification(id, msg)) - .await - .is_err() - { - info!( - "Device event sender closed for device {:?}, stopping event forwarding.", - identifier - ); + Some(DeviceEvent::Notification(id, msg)) => { + if device_event_sender + .send(InternalDeviceEvent::Notification(id, msg)) + .await + .is_err() + { + info!( + "Device event sender closed for device {:?}, stopping event forwarding.", + identifier + ); + break; + } + } + None => { + // Stream ended (device likely disconnected) break; } } - None => { - // Stream ended (device likely disconnected) - break; - } } - } - }); + }, + info_span!("DeviceEventForwardingTask").or_current(), + ); Ok(device_handle) } diff --git a/crates/buttplug_server/src/device/device_task.rs b/crates/buttplug_server/src/device/device_task.rs index 3ed84f3f3..9cbb6f1ad 100644 --- a/crates/buttplug_server/src/device/device_task.rs +++ b/crates/buttplug_server/src/device/device_task.rs @@ -12,15 +12,12 @@ //! - Keepalive packet management //! - Hardware disconnect detection -use std::{collections::VecDeque, sync::Arc, time::Duration}; +use std::{collections::VecDeque, sync::Arc, time::{Duration, Instant}}; -use buttplug_core::util::{self, async_manager}; +use buttplug_core::util::async_manager; use futures::future; -use tokio::{ - select, - sync::mpsc::Receiver, - time::Instant, -}; +use tokio::{select, sync::mpsc::Receiver}; +use tracing::info_span; use super::{ hardware::{Hardware, HardwareCommand, HardwareEvent, HardwareWriteCmd}, @@ -52,9 +49,12 @@ pub fn spawn_device_task( config: DeviceTaskConfig, mut command_receiver: Receiver>, ) { - async_manager::spawn(async move { - run_device_task(hardware, config, &mut command_receiver).await; - }); + async_manager::spawn( + async move { + run_device_task(hardware, config, &mut command_receiver).await; + }, + info_span!("DeviceTask"), + ); } /// Run the device communication task (internal implementation). @@ -98,9 +98,9 @@ async fn run_device_task( // Calculate keepalive timeout let keepalive_fut = async { if let Some(duration) = strategy_duration { - util::sleep(duration).await; + async_manager::sleep(duration).await; } else if requires_keepalive { - util::sleep(Duration::from_secs(5)).await; // iOS Bluetooth default + async_manager::sleep(Duration::from_secs(5)).await; // iOS Bluetooth default } else { future::pending::<()>().await; } @@ -109,7 +109,7 @@ async fn run_device_task( // Calculate batch flush timeout (only if we're batching) let batch_fut = async { match batch_deadline { - Some(deadline) => tokio::time::sleep_until(deadline).await, + Some(deadline) => async_manager::sleep_until(deadline).await, None => future::pending::<()>().await, } }; diff --git a/crates/buttplug_server/src/device/hardware/communication.rs b/crates/buttplug_server/src/device/hardware/communication.rs index a9d521977..95b712f03 100644 --- a/crates/buttplug_server/src/device/hardware/communication.rs +++ b/crates/buttplug_server/src/device/hardware/communication.rs @@ -8,7 +8,7 @@ use crate::device::hardware::HardwareConnector; use async_trait::async_trait; use buttplug_core::{ - util::{async_manager, sleep}, + util::async_manager, {ButtplugResultFuture, errors::ButtplugDeviceError}, }; use futures::future::{self, FutureExt}; @@ -17,6 +17,7 @@ use std::{sync::Arc, time::Duration}; use thiserror::Error; use tokio::sync::mpsc::Sender; use tokio_util::sync::CancellationToken; +use tracing::info_span; #[derive(Debug)] pub enum HardwareCommunicationManagerEvent { @@ -95,18 +96,21 @@ impl HardwareCommunicationManager self.cancellation_token = Some(token); let duration = self.comm_manager.rescan_wait_duration(); async move { - async_manager::spawn(async move { - loop { - if let Err(err) = comm_manager.scan().await { - error!("Timed Device Communication Manager Failure: {}", err); - break; + async_manager::spawn( + async move { + loop { + if let Err(err) = comm_manager.scan().await { + error!("Timed Device Communication Manager Failure: {}", err); + break; + } + tokio::select! { + _ = async_manager::sleep(duration) => continue, + _ = child_token.cancelled() => break, + } } - tokio::select! { - _ = sleep(duration) => continue, - _ = child_token.cancelled() => break, - } - } - }); + }, + info_span!("TimedRetryCommunicationManagerScanLoop").or_current(), + ); Ok(()) } .boxed() diff --git a/crates/buttplug_server/src/device/mod.rs b/crates/buttplug_server/src/device/mod.rs index 93e3d5872..921c6bf3c 100644 --- a/crates/buttplug_server/src/device/mod.rs +++ b/crates/buttplug_server/src/device/mod.rs @@ -119,5 +119,4 @@ pub(crate) enum InternalDeviceEvent { /// A device has disconnected Disconnected(UserDeviceIdentifier), } -pub use protocol_impl::get_default_protocol_map; pub use server_device_manager::{ServerDeviceManager, ServerDeviceManagerBuilder}; diff --git a/crates/buttplug_server/src/device/protocol.rs b/crates/buttplug_server/src/device/protocol.rs index e624b5706..762223060 100644 --- a/crates/buttplug_server/src/device/protocol.rs +++ b/crates/buttplug_server/src/device/protocol.rs @@ -17,13 +17,14 @@ use buttplug_server_device_config::{ ServerDeviceDefinition, UserDeviceIdentifier, }; +use compact_str::CompactString; use dashmap::DashMap; use super::hardware::HardwareWriteCmd; use crate::{ device::{ hardware::{Hardware, HardwareCommand, HardwareReadCmd}, - protocol_impl::get_default_protocol_map, + protocol_impl::get_protocol_identifier, }, message::{ ButtplugServerDeviceMessage, @@ -132,22 +133,22 @@ pub trait ProtocolInitializer: Sync + Send { ) -> Result, ButtplugDeviceError>; } -pub struct GenericProtocolIdentifier { - handler: Option>, - protocol_identifier: String, +pub struct GenericProtocolIdentifier { + protocol_identifier: &'static str, + marker: std::marker::PhantomData, } -impl GenericProtocolIdentifier { - pub fn new(handler: Arc, protocol_identifier: &str) -> Self { +impl GenericProtocolIdentifier { + pub fn new(protocol_identifier: &'static str) -> Self { Self { - handler: Some(handler), - protocol_identifier: protocol_identifier.to_owned(), + protocol_identifier, + marker: std::marker::PhantomData, } } } #[async_trait] -impl ProtocolIdentifier for GenericProtocolIdentifier { +impl ProtocolIdentifier for GenericProtocolIdentifier { async fn identify( &mut self, hardware: Arc, @@ -156,13 +157,11 @@ impl ProtocolIdentifier for GenericProtocolIdentifier { let device_identifier = UserDeviceIdentifier::new( hardware.address(), &self.protocol_identifier, - &Some(hardware.name().to_owned()), + Some(hardware.name()), ); Ok(( device_identifier, - Box::new(GenericProtocolInitializer::new( - self.handler.take().unwrap(), - )), + Box::new(GenericProtocolInitializer::new(Arc::new(T::default()))), )) } } @@ -199,22 +198,7 @@ pub trait ProtocolHandler: Sync + Send { &self, message: &ButtplugDeviceCommandMessageUnionV4, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented(print_type_of(&message)) - } - - // Allow here since this changes between debug/release - #[allow(unused_variables)] - fn command_unimplemented( - &self, - command: &str, - ) -> Result, ButtplugDeviceError> { - #[cfg(debug_assertions)] - unimplemented!("Command not implemented for this protocol"); - #[cfg(not(debug_assertions))] - Err(ButtplugDeviceError::UnhandledCommand(format!( - "Command not implemented for this protocol: {}", - command - ))) + command_unimplemented(print_type_of(&message)) } // The default scalar handler assumes that most devices require discrete commands per feature. If @@ -222,65 +206,9 @@ pub trait ProtocolHandler: Sync + Send { // actuators, they should just implement their own version of this method. fn handle_output_cmd( &self, - cmd: &CheckedOutputCmdV4, - ) -> Result, ButtplugDeviceError> { - let output_command = cmd.output_command(); - match output_command { - OutputCommand::Constrict(x) => self.handle_output_constrict_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value() - .try_into() - .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, - ), - OutputCommand::Spray(x) => self.handle_output_spray_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value() - .try_into() - .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, - ), - OutputCommand::Oscillate(x) => self.handle_output_oscillate_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value() - .try_into() - .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, - ), - OutputCommand::Rotate(x) => { - self.handle_output_rotate_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) - } - OutputCommand::Vibrate(x) => self.handle_output_vibrate_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value() - .try_into() - .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, - ), - OutputCommand::Position(x) => self.handle_output_position_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value() - .try_into() - .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, - ), - OutputCommand::Temperature(x) => { - self.handle_output_temperature_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) - } - OutputCommand::Led(x) => self.handle_output_led_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value() - .try_into() - .map_err(|_| ButtplugDeviceError::DeviceCommandSignError)?, - ), - OutputCommand::HwPositionWithDuration(x) => self.handle_hw_position_with_duration_cmd( - cmd.feature_index(), - cmd.feature_id(), - x.value(), - x.duration(), - ), - } + _cmd: &CheckedOutputCmdV4, + ) -> Option, ButtplugDeviceError>> { + None } fn handle_output_vibrate_cmd( @@ -289,7 +217,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _speed: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Vibrate Actuator)") + command_unimplemented("OutputCmd (Vibrate Actuator)") } fn handle_output_rotate_cmd( @@ -298,7 +226,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _speed: i32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Rotate Actuator)") + command_unimplemented("OutputCmd (Rotate Actuator)") } fn handle_output_oscillate_cmd( @@ -307,7 +235,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _speed: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Oscillate Actuator)") + command_unimplemented("OutputCmd (Oscillate Actuator)") } fn handle_output_spray_cmd( @@ -316,7 +244,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _level: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Spray Actuator)") + command_unimplemented("OutputCmd (Spray Actuator)") } fn handle_output_constrict_cmd( @@ -325,7 +253,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _level: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Constrict Actuator)") + command_unimplemented("OutputCmd (Constrict Actuator)") } fn handle_output_temperature_cmd( @@ -334,7 +262,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _level: i32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Temperature Actuator)") + command_unimplemented("OutputCmd (Temperature Actuator)") } fn handle_output_led_cmd( @@ -343,7 +271,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _level: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Led Actuator)") + command_unimplemented("OutputCmd (Led Actuator)") } fn handle_output_position_cmd( @@ -352,7 +280,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _position: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Position Actuator)") + command_unimplemented("OutputCmd (Position Actuator)") } fn handle_hw_position_with_duration_cmd( @@ -362,7 +290,7 @@ pub trait ProtocolHandler: Sync + Send { _position: u32, _duration: u32, ) -> Result, ButtplugDeviceError> { - self.command_unimplemented("OutputCmd (Position w/ Duration Actuator)") + command_unimplemented("OutputCmd (Position w/ Duration Actuator)") } fn handle_input_subscribe_cmd( @@ -373,10 +301,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _sensor_type: InputType, ) -> BoxFuture<'_, Result<(), ButtplugDeviceError>> { - future::ready(Err(ButtplugDeviceError::UnhandledCommand( - "Command not implemented for this protocol: InputCmd (Subscribe)".to_string(), - ))) - .boxed() + command_unimplemented_future("InputCmd (Subscribe)") } fn handle_input_unsubscribe_cmd( @@ -386,10 +311,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_id: Uuid, _sensor_type: InputType, ) -> BoxFuture<'_, Result<(), ButtplugDeviceError>> { - future::ready(Err(ButtplugDeviceError::UnhandledCommand( - "Command not implemented for this protocol: InputCmd (Unsubscribe)".to_string(), - ))) - .boxed() + command_unimplemented_future("InputCmd (Unsubscribe)") } fn handle_input_read_cmd( @@ -400,14 +322,10 @@ pub trait ProtocolHandler: Sync + Send { feature_id: Uuid, sensor_type: InputType, ) -> BoxFuture<'_, Result> { - match sensor_type { - InputType::Battery => { - self.handle_battery_level_cmd(device_index, device, feature_index, feature_id) - } - _ => future::ready(Err(ButtplugDeviceError::UnhandledCommand( - "Command not implemented for this protocol: InputCmd (Read)".to_string(), - ))) - .boxed(), + if sensor_type == InputType::Battery { + self.handle_battery_level_cmd(device_index, device, feature_index, feature_id) + } else { + command_unimplemented_future("InputCmd (Read)") } } @@ -427,30 +345,7 @@ pub trait ProtocolHandler: Sync + Send { feature_index: u32, feature_id: Uuid, ) -> BoxFuture<'_, Result> { - // If we have a standardized BLE Battery endpoint, handle that above the - // protocol, as it'll always be the same. - if device.endpoints().contains(&Endpoint::RxBLEBattery) { - debug!("Trying to get battery reading."); - let msg = HardwareReadCmd::new(feature_id, Endpoint::RxBLEBattery, 1, 0); - let fut = device.read_value(&msg); - async move { - let hw_msg = fut.await?; - let battery_level = hw_msg.data()[0] as i32; - let battery_reading = InputReadingV4::new( - device_index, - feature_index, - buttplug_core::message::InputTypeReading::Battery(InputValue::new(battery_level as u8)), - ); - debug!("Got battery reading: {}", battery_level); - Ok(battery_reading) - } - .boxed() - } else { - future::ready(Err(ButtplugDeviceError::UnhandledCommand( - "Command not implemented for this protocol: SensorReadCmd".to_string(), - ))) - .boxed() - } + default_handle_battery_level_cmd(device_index, device, feature_index, feature_id) } fn handle_rssi_level_cmd( @@ -459,10 +354,7 @@ pub trait ProtocolHandler: Sync + Send { _feature_index: u32, _feature_id: Uuid, ) -> BoxFuture<'_, Result<(), ButtplugDeviceError>> { - future::ready(Err(ButtplugDeviceError::UnhandledCommand( - "Command not implemented for this protocol: SensorReadCmd".to_string(), - ))) - .boxed() + command_unimplemented_future("SensorReadCmd") } fn event_stream( @@ -472,30 +364,73 @@ pub trait ProtocolHandler: Sync + Send { } } +// Allow here since this changes between debug/release +#[allow(unused_variables)] +fn command_unimplemented(command: &str) -> Result { + #[cfg(debug_assertions)] + unimplemented!("Command not implemented for this protocol"); + #[cfg(not(debug_assertions))] + Err(ButtplugDeviceError::UnhandledCommand(format!( + "Command not implemented for this protocol: {}", + command + ))) +} + +// Allow here since this changes between debug/release +#[allow(unused_variables)] +fn command_unimplemented_future( + command: &str, +) -> BoxFuture<'static, Result> { + #[cfg(debug_assertions)] + unimplemented!("Command not implemented for this protocol"); + #[cfg(not(debug_assertions))] + future::ready(Err(ButtplugDeviceError::UnhandledCommand(format!( + "Command not implemented for this protocol: {}", + command + )))) + .boxed() +} + +// Free function — one copy shared by all 135 vtables +pub(crate) fn default_handle_battery_level_cmd( + device_index: u32, + device: Arc, + feature_index: u32, + feature_id: Uuid, +) -> BoxFuture<'static, Result> { + if device.endpoints().contains(&Endpoint::RxBLEBattery) { + debug!("Trying to get battery reading."); + let msg = HardwareReadCmd::new(feature_id, Endpoint::RxBLEBattery, 1, 0); + let fut = device.read_value(&msg); + async move { + let hw_msg = fut.await?; + let battery_level = hw_msg.data()[0] as i32; + let battery_reading = InputReadingV4::new( + device_index, + feature_index, + buttplug_core::message::InputTypeReading::Battery(InputValue::new(battery_level as u8)), + ); + debug!("Got battery reading: {}", battery_level); + Ok(battery_reading) + } + .boxed() + } else { + command_unimplemented_future("SensorReadCmd") + } +} + #[macro_export] macro_rules! generic_protocol_setup { ( $protocol_name:ident, $protocol_identifier:tt) => { - paste::paste! { - pub mod setup { - use std::sync::Arc; - use $crate::device::protocol::{ - GenericProtocolIdentifier, ProtocolIdentifier, ProtocolIdentifierFactory, - }; - #[derive(Default)] - pub struct [< $protocol_name IdentifierFactory >] {} - - impl ProtocolIdentifierFactory for [< $protocol_name IdentifierFactory >] { - fn identifier(&self) -> &str { - $protocol_identifier - } - - fn create(&self) -> Box { - Box::new(GenericProtocolIdentifier::new( - Arc::new(super::$protocol_name::default()), - self.identifier(), - )) - } - } + pub mod setup { + use $crate::device::protocol::{GenericProtocolIdentifier, ProtocolIdentifier}; + + pub const IDENTIFIER: &str = $protocol_identifier; + + pub fn create_identifier() -> Box { + Box::new(GenericProtocolIdentifier::::new( + IDENTIFIER, + )) } } }; @@ -506,18 +441,12 @@ macro_rules! generic_protocol_initializer_setup { ( $protocol_name:ident, $protocol_identifier:tt) => { paste::paste! { pub mod setup { - use $crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct [< $protocol_name IdentifierFactory >] {} - - impl ProtocolIdentifierFactory for [< $protocol_name IdentifierFactory >] { - fn identifier(&self) -> &str { - $protocol_identifier - } - - fn create(&self) -> Box { - Box::new(super::[< $protocol_name Identifier >]::default()) - } + use $crate::device::protocol::ProtocolIdentifier; + + pub const IDENTIFIER: &str = $protocol_identifier; + + pub fn create_identifier() -> Box { + Box::new(super::[< $protocol_name Identifier >]::default()) } } @@ -531,7 +460,7 @@ macro_rules! generic_protocol_initializer_setup { hardware: Arc, _: ProtocolCommunicationSpecifier, ) -> Result<(UserDeviceIdentifier, Box), ButtplugDeviceError> { - Ok((UserDeviceIdentifier::new(hardware.address(), $protocol_identifier, &Some(hardware.name().to_owned())), Box::new([< $protocol_name Initializer >]::default()))) + Ok((UserDeviceIdentifier::new(hardware.address(), $protocol_identifier, Some(hardware.name())), Box::new([< $protocol_name Initializer >]::default()))) } } } @@ -541,64 +470,41 @@ macro_rules! generic_protocol_initializer_setup { pub use generic_protocol_initializer_setup; pub use generic_protocol_setup; -pub struct ProtocolManager { - // Map of protocol names to their respective protocol instance factories - protocol_map: HashMap>, -} - -impl Default for ProtocolManager { - fn default() -> Self { - Self { - protocol_map: get_default_protocol_map(), - } - } -} - -impl ProtocolManager { - pub fn protocol_specializers( - &self, - specifier: &ProtocolCommunicationSpecifier, - base_communication_specifiers: &HashMap>, - user_communication_specifiers: &DashMap>, - ) -> Vec { - debug!( - "Looking for protocol that matches specifier: {:?}", - specifier - ); - let mut specializers = vec![]; - let mut update_specializer_map = - |name: &str, specifiers: &Vec| { - if specifiers.contains(specifier) { - info!( - "Found protocol {:?} for user specifier {:?}.", +pub fn get_protocol_specializers( + specifier: &ProtocolCommunicationSpecifier, + base_communication_specifiers: &HashMap>, + user_communication_specifiers: &DashMap>, +) -> Vec { + debug!( + "Looking for protocol that matches specifier: {:?}", + specifier + ); + let mut specializers = vec![]; + let mut update_specializer_map = + |name: &str, specifiers: &Vec| { + if specifiers.contains(specifier) { + info!( + "Found protocol {:?} for user specifier {:?}.", + name, specifier + ); + if let Some(identifier) = get_protocol_identifier(name) { + specializers.push(ProtocolSpecializer::new(specifiers.clone(), identifier)); + } else { + warn!( + "No protocol implementation for {:?} found for specifier {:?}.", name, specifier ); - if self.protocol_map.contains_key(name) { - specializers.push(ProtocolSpecializer::new( - specifiers.clone(), - self - .protocol_map - .get(name) - .expect("already checked existence") - .create(), - )); - } else { - warn!( - "No protocol implementation for {:?} found for specifier {:?}.", - name, specifier - ); - } } - }; - // Loop through both maps, as chaining between DashMap and HashMap gets kinda gross. - for spec in user_communication_specifiers.iter() { - update_specializer_map(spec.key(), spec.value()); - } - for (name, specifiers) in base_communication_specifiers.iter() { - update_specializer_map(name, specifiers); - } - specializers + } + }; + // Loop through both maps, as chaining between DashMap and HashMap gets kinda gross. + for spec in user_communication_specifiers.iter() { + update_specializer_map(spec.key(), spec.value()); + } + for (name, specifiers) in base_communication_specifiers.iter() { + update_specializer_map(name, specifiers); } + specializers } /* diff --git a/crates/buttplug_server/src/device/protocol_impl/cowgirl_cone.rs b/crates/buttplug_server/src/device/protocol_impl/cowgirl_cone.rs index c9201e95b..dfeedd0b6 100644 --- a/crates/buttplug_server/src/device/protocol_impl/cowgirl_cone.rs +++ b/crates/buttplug_server/src/device/protocol_impl/cowgirl_cone.rs @@ -15,7 +15,7 @@ use crate::device::{ }, }; use async_trait::async_trait; -use buttplug_core::{errors::ButtplugDeviceError, util::sleep}; +use buttplug_core::{errors::ButtplugDeviceError, util::async_manager}; use buttplug_server_device_config::{ Endpoint, ProtocolCommunicationSpecifier, @@ -47,7 +47,7 @@ impl ProtocolInitializer for CowgirlConeInitializer { false, )) .await?; - sleep(Duration::from_millis(3000)).await; + async_manager::sleep(Duration::from_millis(3000)).await; Ok(Arc::new(CowgirlCone::default())) } } diff --git a/crates/buttplug_server/src/device/protocol_impl/fluffer.rs b/crates/buttplug_server/src/device/protocol_impl/fluffer.rs index e3015a4a8..c8f6f79f6 100644 --- a/crates/buttplug_server/src/device/protocol_impl/fluffer.rs +++ b/crates/buttplug_server/src/device/protocol_impl/fluffer.rs @@ -5,9 +5,17 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use crate::device::{ - hardware::{Hardware, HardwareCommand, HardwareEvent, HardwareSubscribeCmd, HardwareWriteCmd}, - protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer, ProtocolKeepaliveStrategy}, +use crate::{ + device::{ + hardware::{Hardware, HardwareCommand, HardwareEvent, HardwareSubscribeCmd, HardwareWriteCmd}, + protocol::{ + ProtocolHandler, + ProtocolIdentifier, + ProtocolInitializer, + ProtocolKeepaliveStrategy, + }, + }, + generic_protocol_setup, }; use aes::Aes128; use async_trait::async_trait; @@ -34,21 +42,7 @@ type Aes128EcbDec = ecb::Decryptor; const FLUFFER_PROTOCOL_UUID: Uuid = uuid!("d3721a71-a81d-461a-b404-8599ce50c00b"); const FLUFFER_KEY: [u8; 16] = *b"jdk#Flu%y6fer32f"; -pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct FlufferIdentifierFactory {} - - impl ProtocolIdentifierFactory for FlufferIdentifierFactory { - fn identifier(&self) -> &str { - "fluffer" - } - - fn create(&self) -> Box { - Box::new(super::FlufferIdentifier::default()) - } - } -} +generic_protocol_setup!(Fluffer, "fluffer"); #[derive(Default)] pub struct FlufferIdentifier {} @@ -81,11 +75,7 @@ impl ProtocolIdentifier for FlufferIdentifier { ); } Ok(( - UserDeviceIdentifier::new( - hardware.address(), - "fluffer", - &Some(hardware.name().to_owned()), - ), + UserDeviceIdentifier::new(hardware.address(), "fluffer", Some(hardware.name())), Box::new(FlufferInitializer::new(data)), )) } diff --git a/crates/buttplug_server/src/device/protocol_impl/fredorch.rs b/crates/buttplug_server/src/device/protocol_impl/fredorch.rs index 2c03bffbd..9b85fbeea 100644 --- a/crates/buttplug_server/src/device/protocol_impl/fredorch.rs +++ b/crates/buttplug_server/src/device/protocol_impl/fredorch.rs @@ -15,7 +15,7 @@ use crate::device::{ }, }; use async_trait::async_trait; -use buttplug_core::{errors::ButtplugDeviceError, util::sleep}; +use buttplug_core::{errors::ButtplugDeviceError, util::async_manager}; use buttplug_server_device_config::{ Endpoint, ProtocolCommunicationSpecifier, @@ -137,7 +137,7 @@ impl ProtocolInitializer for FredorchInitializer { ); } } - _ = sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { + _ = async_manager::sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { // Or not? } } @@ -169,7 +169,7 @@ impl ProtocolInitializer for FredorchInitializer { ); } } - _ = sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { + _ = async_manager::sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { return Err( ButtplugDeviceError::ProtocolSpecificError( "Fredorch".to_owned(), diff --git a/crates/buttplug_server/src/device/protocol_impl/fredorch_rotary.rs b/crates/buttplug_server/src/device/protocol_impl/fredorch_rotary.rs index f63111b94..efa6ff8d0 100644 --- a/crates/buttplug_server/src/device/protocol_impl/fredorch_rotary.rs +++ b/crates/buttplug_server/src/device/protocol_impl/fredorch_rotary.rs @@ -14,10 +14,7 @@ use crate::device::{ }, }; use async_trait::async_trait; -use buttplug_core::{ - errors::ButtplugDeviceError, - util::{async_manager, sleep}, -}; +use buttplug_core::{errors::ButtplugDeviceError, util::async_manager}; use buttplug_server_device_config::{ Endpoint, ProtocolCommunicationSpecifier, @@ -33,6 +30,7 @@ use std::{ time::Duration, }; use tokio::select; +use tracing::info_span; use uuid::{Uuid, uuid}; const FREDORCH_COMMAND_TIMEOUT_MS: u64 = 100; @@ -106,7 +104,7 @@ impl ProtocolInitializer for FredorchRotaryInitializer { ); } } - _ = sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { + _ = async_manager::sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).fuse() => { // The after the password check, we won't get anything } } @@ -183,7 +181,7 @@ async fn speed_update_handler( } } - sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).await; + async_manager::sleep(Duration::from_millis(FREDORCH_COMMAND_TIMEOUT_MS)).await; } info!("FredorchRotary control loop exiting, most likely due to device disconnection."); } @@ -194,9 +192,10 @@ impl FredorchRotary { let target_speed = Arc::new(AtomicU8::new(0)); let current_speed_clone = current_speed.clone(); let target_speed_clone = target_speed.clone(); - async_manager::spawn(async move { - speed_update_handler(device, current_speed_clone, target_speed_clone).await - }); + async_manager::spawn( + async move { speed_update_handler(device, current_speed_clone, target_speed_clone).await }, + info_span!("FredorchRotaryControlLoop").or_current(), + ); Self { current_speed, target_speed, diff --git a/crates/buttplug_server/src/device/protocol_impl/hgod.rs b/crates/buttplug_server/src/device/protocol_impl/hgod.rs index 1beecd570..0b22e302b 100644 --- a/crates/buttplug_server/src/device/protocol_impl/hgod.rs +++ b/crates/buttplug_server/src/device/protocol_impl/hgod.rs @@ -15,10 +15,7 @@ use crate::device::{ }, }; use async_trait::async_trait; -use buttplug_core::{ - errors::ButtplugDeviceError, - util::{async_manager, sleep}, -}; +use buttplug_core::{errors::ButtplugDeviceError, util::async_manager}; use buttplug_server_device_config::{ Endpoint, ProtocolCommunicationSpecifier, @@ -32,6 +29,7 @@ use std::{ }, time::Duration, }; +use tracing::info_span; use uuid::{Uuid, uuid}; // Time between Hgod update commands, in milliseconds. @@ -63,9 +61,12 @@ impl Hgod { let last_command = Arc::new(AtomicU8::new(0)); let last_command_clone = last_command.clone(); - async_manager::spawn(async move { - send_hgod_updates(hardware, last_command_clone).await; - }); + async_manager::spawn( + async move { + send_hgod_updates(hardware, last_command_clone).await; + }, + info_span!("Hgod::send_hgod_updates").or_current(), + ); Self { last_command } } @@ -92,7 +93,7 @@ async fn send_hgod_updates(device: Arc, data: Arc) { ); break; } - sleep(Duration::from_millis(HGOD_COMMAND_DELAY_MS)).await; + async_manager::sleep(Duration::from_millis(HGOD_COMMAND_DELAY_MS)).await; } } diff --git a/crates/buttplug_server/src/device/protocol_impl/hismith.rs b/crates/buttplug_server/src/device/protocol_impl/hismith.rs index 99183c75b..728193bd6 100644 --- a/crates/buttplug_server/src/device/protocol_impl/hismith.rs +++ b/crates/buttplug_server/src/device/protocol_impl/hismith.rs @@ -6,9 +6,12 @@ // for full license information. use super::hismith_mini::HismithMiniInitializer; -use crate::device::{ - hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd}, - protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer}, +use crate::{ + device::{ + hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd}, + protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer}, + }, + generic_protocol_setup, }; use async_trait::async_trait; use buttplug_core::errors::ButtplugDeviceError; @@ -23,21 +26,7 @@ use uuid::{Uuid, uuid}; const HISMITH_PROTOCOL_UUID: Uuid = uuid!("e59f9c5d-bb4a-4a9c-ab57-0ceb43af1da7"); -pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct HismithIdentifierFactory {} - - impl ProtocolIdentifierFactory for HismithIdentifierFactory { - fn identifier(&self) -> &str { - "hismith" - } - - fn create(&self) -> Box { - Box::new(super::HismithIdentifier::default()) - } - } -} +generic_protocol_setup!(Hismith, "hismith"); #[derive(Default)] pub struct HismithIdentifier {} @@ -70,13 +59,13 @@ impl ProtocolIdentifier for HismithIdentifier { if !LEGACY_HISMITHS.contains(&identifier.as_str()) { info!("Not a legacy Hismith, using hismith-mini protocol"); return Ok(( - UserDeviceIdentifier::new(hardware.address(), "hismith-mini", &Some(identifier)), + UserDeviceIdentifier::new(hardware.address(), "hismith-mini", Some(&identifier)), Box::new(HismithMiniInitializer::default()), )); } Ok(( - UserDeviceIdentifier::new(hardware.address(), "hismith", &Some(identifier)), + UserDeviceIdentifier::new(hardware.address(), "hismith", Some(&identifier)), Box::new(HismithInitializer::default()), )) } diff --git a/crates/buttplug_server/src/device/protocol_impl/hismith_mini.rs b/crates/buttplug_server/src/device/protocol_impl/hismith_mini.rs index 3358c4608..212426345 100644 --- a/crates/buttplug_server/src/device/protocol_impl/hismith_mini.rs +++ b/crates/buttplug_server/src/device/protocol_impl/hismith_mini.rs @@ -4,9 +4,12 @@ // // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use crate::device::{ - hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd}, - protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer}, +use crate::{ + device::{ + hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd}, + protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer}, + }, + generic_protocol_setup, }; use async_trait::async_trait; use buttplug_core::{errors::ButtplugDeviceError, message::OutputType}; @@ -22,21 +25,7 @@ use uuid::{Uuid, uuid}; const HISMITH_MINI_PROTOCOL_UUID: Uuid = uuid!("94befc1a-9859-4bf6-99ee-5678c89237a7"); const HISMITH_MINI_ROTATE_DIRECTIOM_UUID: Uuid = uuid!("94befc1a-9859-4bf6-99ee-5678c89237a7"); -pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct HismithMiniIdentifierFactory {} - - impl ProtocolIdentifierFactory for HismithMiniIdentifierFactory { - fn identifier(&self) -> &str { - "hismith-mini" - } - - fn create(&self) -> Box { - Box::new(super::HismithMiniIdentifier::default()) - } - } -} +generic_protocol_setup!(HismithMini, "hismith-mini"); #[derive(Default)] pub struct HismithMiniIdentifier {} @@ -65,7 +54,7 @@ impl ProtocolIdentifier for HismithMiniIdentifier { info!("Hismith Device Identifier: {}", identifier); Ok(( - UserDeviceIdentifier::new(hardware.address(), "hismith-mini", &Some(identifier)), + UserDeviceIdentifier::new(hardware.address(), "hismith-mini", Some(&identifier)), Box::new(HismithMiniInitializer::default()), )) } diff --git a/crates/buttplug_server/src/device/protocol_impl/kgoal_boost.rs b/crates/buttplug_server/src/device/protocol_impl/kgoal_boost.rs index b4a6a205d..2137f7774 100644 --- a/crates/buttplug_server/src/device/protocol_impl/kgoal_boost.rs +++ b/crates/buttplug_server/src/device/protocol_impl/kgoal_boost.rs @@ -31,6 +31,7 @@ use std::{ }, }; use tokio::sync::broadcast; +use tracing::info_span; use uuid::Uuid; generic_protocol_setup!(KGoalBoost, "kgoal-boost"); @@ -90,69 +91,76 @@ impl ProtocolHandler for KGoalBoost { let stream_sensors = stream_sensors.clone(); info!("Starting Kgoal subscription"); // If we subscribe successfully, we need to set up our event handler. - async_manager::spawn(async move { - let mut cached_values = vec![0u32, 0u32]; - while let Ok(info) = hardware_stream.recv().await { - let subscribed_sensors = stream_sensors.load(Ordering::Relaxed); - // If we have no receivers, quit. - if sender.receiver_count() == 0 || subscribed_sensors == 0 { - return; - } - if let HardwareEvent::Notification(_, endpoint, data) = info - && endpoint == Endpoint::RxPressure - { - if data.len() < 7 { - // Not even sure how this would happen, error and continue on. - error!("KGoal Boost data not expected length!"); - continue; - } - // Extract our two pressure values. - let normalized = (data[3] as u32) << 8 | data[4] as u32; - let unnormalized = (data[5] as u32) << 8 | data[6] as u32; - info!( - "Kgoal Reading {} {} {}", - subscribed_sensors, normalized, unnormalized - ); - if (subscribed_sensors & (1 << 0)) > 0 - && cached_values[0] != normalized - && sender - .send( - InputReadingV4::new( - device_index, - 0, - buttplug_core::message::InputTypeReading::Pressure(InputValue::new( - normalized, - )), - ) - .into(), - ) - .is_err() - { - debug!("Hardware device listener for KGoal Boost shut down, returning from task."); + async_manager::spawn( + async move { + let mut cached_values = vec![0u32, 0u32]; + while let Ok(info) = hardware_stream.recv().await { + let subscribed_sensors = stream_sensors.load(Ordering::Relaxed); + // If we have no receivers, quit. + if sender.receiver_count() == 0 || subscribed_sensors == 0 { return; } - if (subscribed_sensors & (1 << 1)) > 0 - && cached_values[1] != unnormalized - && sender - .send( - InputReadingV4::new( - device_index, - 1, - buttplug_core::message::InputTypeReading::Pressure(InputValue::new( - unnormalized, - )), - ) - .into(), - ) - .is_err() + if let HardwareEvent::Notification(_, endpoint, data) = info + && endpoint == Endpoint::RxPressure { - debug!("Hardware device listener for KGoal Boost shut down, returning from task."); - return; + if data.len() < 7 { + // Not even sure how this would happen, error and continue on. + error!("KGoal Boost data not expected length!"); + continue; + } + // Extract our two pressure values. + let normalized = (data[3] as u32) << 8 | data[4] as u32; + let unnormalized = (data[5] as u32) << 8 | data[6] as u32; + info!( + "Kgoal Reading {} {} {}", + subscribed_sensors, normalized, unnormalized + ); + if (subscribed_sensors & (1 << 0)) > 0 + && cached_values[0] != normalized + && sender + .send( + InputReadingV4::new( + device_index, + 0, + buttplug_core::message::InputTypeReading::Pressure(InputValue::new( + normalized, + )), + ) + .into(), + ) + .is_err() + { + debug!( + "Hardware device listener for KGoal Boost shut down, returning from task." + ); + return; + } + if (subscribed_sensors & (1 << 1)) > 0 + && cached_values[1] != unnormalized + && sender + .send( + InputReadingV4::new( + device_index, + 1, + buttplug_core::message::InputTypeReading::Pressure(InputValue::new( + unnormalized, + )), + ) + .into(), + ) + .is_err() + { + debug!( + "Hardware device listener for KGoal Boost shut down, returning from task." + ); + return; + } + cached_values = vec![normalized, unnormalized]; } - cached_values = vec![normalized, unnormalized]; } - } - }); + }, + info_span!("KGoalBoost::handle_input_subscribe_cmd event handler").or_current(), + ); } stream_sensors.store( stream_sensors.load(Ordering::Relaxed) | (1 << feature_index), diff --git a/crates/buttplug_server/src/device/protocol_impl/lovense/lovense_stroker.rs b/crates/buttplug_server/src/device/protocol_impl/lovense/lovense_stroker.rs index beb60046d..49468d6cc 100644 --- a/crates/buttplug_server/src/device/protocol_impl/lovense/lovense_stroker.rs +++ b/crates/buttplug_server/src/device/protocol_impl/lovense/lovense_stroker.rs @@ -12,7 +12,7 @@ use crate::device::{ use buttplug_core::{ errors::ButtplugDeviceError, message::InputReadingV4, - util::{async_manager, sleep}, + util::async_manager, }; use buttplug_server_device_config::Endpoint; use futures::future::BoxFuture; @@ -23,6 +23,7 @@ use std::{ }, time::Duration, }; +use tracing::info_span; use uuid::{Uuid, uuid}; const LOVENSE_STROKER_PROTOCOL_UUID: Uuid = uuid!("a97fc354-5561-459a-bc62-110d7c2868ac"); @@ -35,10 +36,10 @@ pub struct LovenseStroker { impl LovenseStroker { pub fn new(hardware: Arc, need_range_zerod: bool) -> Self { let linear_info = Arc::new((AtomicU32::new(0), AtomicU32::new(0))); - async_manager::spawn(update_linear_movement( - hardware.clone(), - linear_info.clone(), - )); + async_manager::spawn( + update_linear_movement(hardware.clone(), linear_info.clone()), + info_span!("LovenseStroker::update_linear_movement").or_current(), + ); Self { linear_info, need_range_zerod, @@ -119,7 +120,7 @@ async fn update_linear_movement(device: Arc, linear_info: Arc<(AtomicU // If we aren't going anywhere, just pause then restart if current_position == last_goal_position { - sleep(Duration::from_millis(100)).await; + async_manager::sleep(Duration::from_millis(100)).await; continue; } @@ -144,6 +145,6 @@ async fn update_linear_movement(device: Arc, linear_info: Arc<(AtomicU if device.write_value(&hardware_cmd).await.is_err() { return; } - sleep(Duration::from_millis(100)).await; + async_manager::sleep(Duration::from_millis(100)).await; } } diff --git a/crates/buttplug_server/src/device/protocol_impl/lovense/mod.rs b/crates/buttplug_server/src/device/protocol_impl/lovense/mod.rs index 168efcb9f..3539f7a29 100644 --- a/crates/buttplug_server/src/device/protocol_impl/lovense/mod.rs +++ b/crates/buttplug_server/src/device/protocol_impl/lovense/mod.rs @@ -27,7 +27,7 @@ use async_trait::async_trait; use buttplug_core::{ errors::ButtplugDeviceError, message::{self, InputReadingV4, InputTypeReading, InputValue, OutputType}, - util::sleep, + util::async_manager, }; use buttplug_server_device_config::{ Endpoint, @@ -36,7 +36,6 @@ use buttplug_server_device_config::{ UserDeviceIdentifier, }; use futures::{FutureExt, future::BoxFuture}; -use regex::Regex; use std::{sync::Arc, time::Duration}; use tokio::select; use uuid::{Uuid, uuid}; @@ -52,18 +51,12 @@ const LOVENSE_COMMAND_RETRY: u64 = 5; const LOVENSE_PROTOCOL_UUID: Uuid = uuid!("cfa3fac5-48bb-4d87-817e-a439965956e1"); pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct LovenseIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; + + pub const IDENTIFIER: &str = "lovense"; - impl ProtocolIdentifierFactory for LovenseIdentifierFactory { - fn identifier(&self) -> &str { - "lovense" - } - - fn create(&self) -> Box { - Box::new(super::LovenseIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::LovenseIdentifier::default()) } } @@ -124,7 +117,7 @@ impl ProtocolIdentifier for LovenseIdentifier { let type_response = std::str::from_utf8(&n).map_err(|_| ButtplugDeviceError::ProtocolSpecificError("lovense".to_owned(), "Lovense device init got back non-UTF8 string.".to_owned()))?.to_owned(); debug!("Lovense Device Type Response: {}", type_response); let ident = lovense_model_resolver(type_response); - return Ok((UserDeviceIdentifier::new(hardware.address(), "lovense", &Some(ident.clone())), Box::new(LovenseInitializer::new(ident)))); + return Ok((UserDeviceIdentifier::new(hardware.address(), "lovense", Some(&ident)), Box::new(LovenseInitializer::new(ident)))); } else { return Err( ButtplugDeviceError::ProtocolSpecificError( @@ -134,16 +127,21 @@ impl ProtocolIdentifier for LovenseIdentifier { ); } } - _ = sleep(Duration::from_millis(LOVENSE_COMMAND_TIMEOUT_MS)).fuse() => { + _ = async_manager::sleep(Duration::from_millis(LOVENSE_COMMAND_TIMEOUT_MS)).fuse() => { count += 1; if count > LOVENSE_COMMAND_RETRY { warn!("Lovense Device timed out while getting DeviceType info. ({} retries)", LOVENSE_COMMAND_RETRY); - let re = Regex::new(r"LVS-([A-Z]+)\d+").expect("Static regex shouldn't fail"); - if let Some(caps) = re.captures(hardware.name()) { - info!("Lovense Device identified by BLE name"); - return Ok((UserDeviceIdentifier::new(hardware.address(), "lovense", &Some(caps[1].to_string())), Box::new(LovenseInitializer::new(caps[1].to_string())))); - }; - return Ok((UserDeviceIdentifier::new(hardware.address(), "lovense", &None), Box::new(LovenseInitializer::new("".to_string())))); + if let Some(pos) = hardware.name().find("LVS-") { + let model = hardware.name()[pos + 4..].to_string(); + if !model.is_empty() { + info!("Lovense Device identified by BLE name: {}", model); + return Ok(( + UserDeviceIdentifier::new(hardware.address(), "lovense", Some(&model)), + Box::new(LovenseInitializer::new(model)), + )); + } + } + return Ok((UserDeviceIdentifier::new(hardware.address(), "lovense", None), Box::new(LovenseInitializer::new("".to_string())))); } } } diff --git a/crates/buttplug_server/src/device/protocol_impl/lovense_connect_service.rs b/crates/buttplug_server/src/device/protocol_impl/lovense_connect_service.rs index 3dc39cb4d..4c2abf209 100644 --- a/crates/buttplug_server/src/device/protocol_impl/lovense_connect_service.rs +++ b/crates/buttplug_server/src/device/protocol_impl/lovense_connect_service.rs @@ -31,18 +31,12 @@ use buttplug_server_device_config::ServerDeviceDefinition; const LOVENSE_CONNECT_UUID: Uuid = uuid!("590bfbbf-c3b7-41ae-9679-485b190ffb87"); pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct LovenseConnectIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; - impl ProtocolIdentifierFactory for LovenseConnectIdentifierFactory { - fn identifier(&self) -> &str { - "lovense-connect-service" - } + pub const IDENTIFIER: &str = "lovense-connect-service"; - fn create(&self) -> Box { - Box::new(super::LovenseConnectIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::LovenseConnectIdentifier::default()) } } @@ -60,7 +54,7 @@ impl ProtocolIdentifier for LovenseConnectIdentifier { UserDeviceIdentifier::new( hardware.address(), "lovense-connect-service", - &Some(hardware.name().to_owned()), + Some(hardware.name()), ), Box::new(LovenseConnectServiceInitializer::default()), )) @@ -154,7 +148,7 @@ impl ProtocolHandler for LovenseConnectService { fn handle_output_cmd( &self, cmd: &crate::message::checked_output_cmd::CheckedOutputCmdV4, - ) -> Result, ButtplugDeviceError> { + ) -> Option, ButtplugDeviceError>> { let mut hardware_cmds = vec![]; // We do all of our validity checking during message conversion to checked, so we should be able to skip validity checking here. @@ -179,7 +173,7 @@ impl ProtocolHandler for LovenseConnectService { ) .into(), ); - Ok(hardware_cmds) + Some(Ok(hardware_cmds)) } else if self.thusting_count != 0 && cmd.output_command().as_output_type() == OutputType::Oscillate { @@ -199,7 +193,7 @@ impl ProtocolHandler for LovenseConnectService { ) .into(), ); - Ok(hardware_cmds) + Some(Ok(hardware_cmds)) } else if cmd.output_command().as_output_type() == OutputType::Oscillate { // Only the max has a constriction system, and there's only one, so just parse the first command. /* ~ Sutekh @@ -225,9 +219,9 @@ impl ProtocolHandler for LovenseConnectService { ) .into(), ); - Ok(hardware_cmds) + Some(Ok(hardware_cmds)) } else { - Ok(hardware_cmds) + Some(Ok(hardware_cmds)) } } diff --git a/crates/buttplug_server/src/device/protocol_impl/mod.rs b/crates/buttplug_server/src/device/protocol_impl/mod.rs index ae0e7a5a9..6702e0e5f 100644 --- a/crates/buttplug_server/src/device/protocol_impl/mod.rs +++ b/crates/buttplug_server/src/device/protocol_impl/mod.rs @@ -5,9 +5,7 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use std::{collections::HashMap, sync::Arc}; - -use crate::device::protocol::ProtocolIdentifierFactory; +use crate::device::protocol::ProtocolIdentifier; // Utility mods pub mod fleshlight_launch_helper; @@ -125,453 +123,149 @@ pub mod youcups; pub mod youou; pub mod zalo; -pub fn get_default_protocol_map() -> HashMap> { - let mut map = HashMap::new(); - fn add_to_protocol_map( - map: &mut HashMap>, - factory: T, - ) where - T: ProtocolIdentifierFactory + 'static, - { - let factory = Arc::new(factory); - map.insert(factory.identifier().to_owned(), factory); - } - - add_to_protocol_map( - &mut map, - activejoy::setup::ActiveJoyIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - adrienlastic::setup::AdrienLasticIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - amorelie_joy::setup::AmorelieJoyIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, aneros::setup::AnerosIdentifierFactory::default()); - add_to_protocol_map(&mut map, ankni::setup::AnkniIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - bananasome::setup::BananasomeIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - cachito::setup::CachitoIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - cowgirl::setup::CowgirlIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - cowgirl_cone::setup::CowgirlConeIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, cupido::setup::CupidoIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - deepsire::setup::DeepSireIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - hismith::setup::HismithIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - hismith_mini::setup::HismithMiniIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, htk_bm::setup::HtkBmIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - thehandy::setup::TheHandyIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - thehandy_v3::setup::TheHandyV3IdentifierFactory::default(), - ); - - add_to_protocol_map( - &mut map, - feelingso::setup::FeelingSoIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - fleshy_thrust::setup::FleshyThrustIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - fluffer::setup::FlufferIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, foreo::setup::ForeoIdentifierFactory::default()); - add_to_protocol_map(&mut map, fox::setup::FoxIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - fredorch::setup::FredorchIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - fredorch_rotary::setup::FredorchRotaryIdentifierFactory::default(), - ); - - add_to_protocol_map(&mut map, hgod::setup::HgodIdentifierFactory::default()); - - add_to_protocol_map( - &mut map, - galaku_pump::setup::GalakuPumpIdentifierFactory::default(), - ); - - add_to_protocol_map(&mut map, galaku::setup::GalakuIdentifierFactory::default()); - - add_to_protocol_map(&mut map, itoys::setup::IToysIdentifierFactory::default()); - add_to_protocol_map(&mut map, jejoue::setup::JeJoueIdentifierFactory::default()); - add_to_protocol_map(&mut map, joyhub::setup::JoyHubIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - kiiroo_powershot::setup::KiirooPowerShotIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_prowand::setup::KiirooProWandIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_spot::setup::KiirooSpotIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_v2::setup::KiirooV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_v3::setup::KiirooV3IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_v2_vibrator::setup::KiirooV2VibratorIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_v21::setup::KiirooV21IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - kiiroo_v21_initialized::setup::KiirooV21InitializedIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, kizuna::setup::KizunaIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - lelof1s::setup::LeloF1sIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - lelof1sv2::setup::LeloF1sV2IdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, leten::setup::LetenIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - lelo_harmony::setup::LeloHarmonyIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - libo_elle::setup::LiboElleIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - libo_shark::setup::LiboSharkIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - libo_vibes::setup::LiboVibesIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - lioness::setup::LionessIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, loob::setup::LoobIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - lovehoney_desire::setup::LovehoneyDesireIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - lovedistance::setup::LoveDistanceIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - lovense::setup::LovenseIdentifierFactory::default(), - ); +macro_rules! protocol_match { + ( $var:ident, $( $( $protocol_module:ident )::+ ),* $(,)? ) => { + match $var { + $( + $( $protocol_module )::+::setup::IDENTIFIER => Some($( $protocol_module )::+::setup::create_identifier()), + )* + _ => None, + } + }; +} - add_to_protocol_map( - &mut map, - lovense_connect_service::setup::LovenseConnectIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - lovenuts::setup::LoveNutsIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - luvmazer::setup::LuvmazerIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - magic_motion_v1::setup::MagicMotionV1IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - magic_motion_v2::setup::MagicMotionV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - magic_motion_v3::setup::MagicMotionV3IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - magic_motion_v4::setup::MagicMotionV4IdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, mannuo::setup::ManNuoIdentifierFactory::default()); - add_to_protocol_map(&mut map, maxpro::setup::MaxproIdentifierFactory::default()); - add_to_protocol_map(&mut map, meese::setup::MeeseIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - mizzzee::setup::MizzZeeIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - mizzzee_v2::setup::MizzZeeV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - mizzzee_v3::setup::MizzZeeV3IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - monsterpub::setup::MonsterPubIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - motorbunny::setup::MotorbunnyIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - mysteryvibe::setup::MysteryVibeIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - mysteryvibe_v2::setup::MysteryVibeV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - nexus_revo::setup::NexusRevoIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - nextlevelracing::setup::NextLevelRacingIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - nintendo_joycon::setup::NintendoJoyconIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, nobra::setup::NobraIdentifierFactory::default()); - add_to_protocol_map(&mut map, omobo::setup::OmoboIdentifierFactory::default()); - add_to_protocol_map(&mut map, patoo::setup::PatooIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - picobong::setup::PicobongIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - pink_punch::setup::PinkPunchIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - prettylove::setup::PrettyLoveIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - raw_protocol::setup::RawProtocolIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, realov::setup::RealovIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - sakuraneko::setup::SakuranekoIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - satisfyer::setup::SatisfyerIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, sensee::setup::SenseeIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - sensee_capsule::setup::SenseeCapsuleIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sensee_v2::setup::SenseeV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sexverse_lg389::setup::SexverseLG389IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sexverse_v1::setup::SexverseV1IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sexverse_v2::setup::SexverseV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sexverse_v3::setup::SexverseV3IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sexverse_v4::setup::SexverseV4IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - sexverse_v5::setup::SexverseV5IdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, serveu::setup::ServeUIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - svakom::svakom_avaneo::setup::SvakomAvaNeoIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_alex::setup::SvakomAlexIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_alex_v2::setup::SvakomAlexV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_barnard::setup::SvakomBarnardIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_barney::setup::SvakomBarneyIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_dice::setup::SvakomDiceIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_dt250a::setup::SvakomDT250AIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_iker::setup::SvakomIkerIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_jordan::setup::SvakomJordanIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_pulse::setup::SvakomPulseIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_sam::setup::SvakomSamIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_sam2::setup::SvakomSam2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_suitcase::setup::SvakomSuitcaseIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_tarax::setup::SvakomTaraXIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_v1::setup::SvakomV1IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_v2::setup::SvakomV2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_v3::setup::SvakomV3IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_v4::setup::SvakomV4IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_v5::setup::SvakomV5IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - svakom::svakom_v6::setup::SvakomV6IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - synchro::setup::SynchroIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, tryfun::setup::TryFunIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - tryfun_blackhole::setup::TryFunBlackHoleIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - tryfun_meta2::setup::TryFunMeta2IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - tcode_v03::setup::TCodeV03IdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - vibcrafter::setup::VibCrafterIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - vibratissimo::setup::VibratissimoIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - vorze_sa::setup::VorzeSAIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, wetoy::setup::WeToyIdentifierFactory::default()); - add_to_protocol_map(&mut map, wevibe::setup::WeVibeIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - wevibe8bit::setup::WeVibe8BitIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - wevibe_chorus::setup::WeVibeChorusIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, xibao::setup::XibaoIdentifierFactory::default()); - add_to_protocol_map(&mut map, xinput::setup::XInputIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - xiuxiuda::setup::XiuxiudaIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - xuanhuan::setup::XuanhuanIdentifierFactory::default(), - ); - add_to_protocol_map( - &mut map, - youcups::setup::YoucupsIdentifierFactory::default(), - ); - add_to_protocol_map(&mut map, youou::setup::YououIdentifierFactory::default()); - add_to_protocol_map(&mut map, zalo::setup::ZaloIdentifierFactory::default()); - add_to_protocol_map( - &mut map, - kgoal_boost::setup::KGoalBoostIdentifierFactory::default(), - ); - map +pub fn get_protocol_identifier(protocol_identifier: &str) -> Option> { + protocol_match!( + protocol_identifier, + activejoy, + adrienlastic, + amorelie_joy, + aneros, + ankni, + bananasome, + cachito, + cowgirl, + cowgirl_cone, + cupido, + deepsire, + feelingso, + fleshy_thrust, + fluffer, + foreo, + fox, + fredorch, + fredorch_rotary, + galaku, + galaku_pump, + hgod, + hismith, + hismith_mini, + htk_bm, + itoys, + jejoue, + joyhub, + kgoal_boost, + kiiroo_powershot, + kiiroo_prowand, + kiiroo_spot, + kiiroo_v2, + kiiroo_v21, + kiiroo_v21_initialized, + kiiroo_v2_vibrator, + kiiroo_v3, + kizuna, + lelo_harmony, + lelof1s, + lelof1sv2, + leten, + libo_elle, + libo_shark, + libo_vibes, + lioness, + loob, + lovedistance, + lovehoney_desire, + lovense, + lovense_connect_service, + lovenuts, + luvmazer, + magic_motion_v1, + magic_motion_v2, + magic_motion_v3, + magic_motion_v4, + mannuo, + maxpro, + meese, + mizzzee, + mizzzee_v2, + mizzzee_v3, + monsterpub, + motorbunny, + mysteryvibe, + mysteryvibe_v2, + nextlevelracing, + nexus_revo, + nintendo_joycon, + nobra, + omobo, + patoo, + picobong, + pink_punch, + prettylove, + raw_protocol, + realov, + sakuraneko, + satisfyer, + sensee, + sensee_capsule, + sensee_v2, + serveu, + sexverse_lg389, + sexverse_v1, + sexverse_v2, + sexverse_v3, + sexverse_v4, + sexverse_v5, + svakom::svakom_alex, + svakom::svakom_alex_v2, + svakom::svakom_avaneo, + svakom::svakom_barnard, + svakom::svakom_barney, + svakom::svakom_dice, + svakom::svakom_dt250a, + svakom::svakom_iker, + svakom::svakom_jordan, + svakom::svakom_pulse, + svakom::svakom_sam, + svakom::svakom_sam2, + svakom::svakom_suitcase, + svakom::svakom_tarax, + svakom::svakom_v1, + svakom::svakom_v2, + svakom::svakom_v3, + svakom::svakom_v4, + svakom::svakom_v5, + svakom::svakom_v6, + synchro, + tcode_v03, + thehandy, + thehandy_v3, + tryfun, + tryfun_blackhole, + tryfun_meta2, + vibcrafter, + vibratissimo, + vorze_sa, + wetoy, + wevibe, + wevibe8bit, + wevibe_chorus, + xibao, + xinput, + xiuxiuda, + xuanhuan, + youcups, + youou, + zalo + ) } diff --git a/crates/buttplug_server/src/device/protocol_impl/monsterpub.rs b/crates/buttplug_server/src/device/protocol_impl/monsterpub.rs index e6aa7bb0a..83a7b762b 100644 --- a/crates/buttplug_server/src/device/protocol_impl/monsterpub.rs +++ b/crates/buttplug_server/src/device/protocol_impl/monsterpub.rs @@ -23,18 +23,12 @@ use std::sync::{ use uuid::{Uuid, uuid}; pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct MonsterPubIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; - impl ProtocolIdentifierFactory for MonsterPubIdentifierFactory { - fn identifier(&self) -> &str { - "monsterpub" - } + pub const IDENTIFIER: &str = "monsterpub"; - fn create(&self) -> Box { - Box::new(super::MonsterPubIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::MonsterPubIdentifier::default()) } } @@ -66,12 +60,11 @@ impl ProtocolIdentifier for MonsterPubIdentifier { "MonsterPub device name is non-UTF8 string.".to_owned(), ) })? - .replace("\0", "") - .to_owned(), + .replace("\0", ""), Err(_) => "Unknown".to_string(), }; return Ok(( - UserDeviceIdentifier::new(hardware.address(), "monsterpub", &Some(ident)), + UserDeviceIdentifier::new(hardware.address(), "monsterpub", Some(&ident)), Box::new(MonsterPubInitializer::default()), )); } diff --git a/crates/buttplug_server/src/device/protocol_impl/nintendo_joycon.rs b/crates/buttplug_server/src/device/protocol_impl/nintendo_joycon.rs index c823ff0c8..3a707eca4 100644 --- a/crates/buttplug_server/src/device/protocol_impl/nintendo_joycon.rs +++ b/crates/buttplug_server/src/device/protocol_impl/nintendo_joycon.rs @@ -22,6 +22,7 @@ use buttplug_server_device_config::{ ServerDeviceDefinition, UserDeviceIdentifier, }; +use tracing::info_span; use std::{ sync::{ Arc, @@ -263,36 +264,39 @@ impl NintendoJoycon { let notifier_clone = notifier.clone(); let is_stopped = Arc::new(AtomicBool::new(false)); let is_stopped_clone = is_stopped.clone(); - async_manager::spawn(async move { - #[cfg(feature = "wasm")] - use buttplug_core::util; - loop { - if is_stopped_clone.load(Ordering::Relaxed) { - return; - } - let amp = speed_val_clone.load(Ordering::Relaxed) as f32 / 1000f32; - let rumble = if amp > 0.001 { - Rumble::new(200.0f32, amp) - } else { - Rumble::stop() - }; - - if let Err(_) = - send_command_raw(hardware.clone(), 1, 16, 0, &[], Some(rumble), Some(rumble)).await - { - error!("Joycon command failed, exiting update loop"); - break; - } - #[cfg(not(feature = "wasm"))] - let _ = tokio::time::timeout(Duration::from_millis(15), notifier_clone.notified()).await; - - // If we're using WASM, we can't use tokio's timeout due to lack of time library in WASM. - // I'm also too lazy to make this a select. So, this'll do. We can't even access this - // protocol in a web context yet since there's no WebHID comm manager yet. + async_manager::spawn( + async move { #[cfg(feature = "wasm")] - util::sleep(Duration::from_millis(15)).await; - } - }); + use buttplug_core::util; + loop { + if is_stopped_clone.load(Ordering::Relaxed) { + return; + } + let amp = speed_val_clone.load(Ordering::Relaxed) as f32 / 1000f32; + let rumble = if amp > 0.001 { + Rumble::new(200.0f32, amp) + } else { + Rumble::stop() + }; + + if let Err(_) = + send_command_raw(hardware.clone(), 1, 16, 0, &[], Some(rumble), Some(rumble)).await + { + error!("Joycon command failed, exiting update loop"); + break; + } + #[cfg(not(feature = "wasm"))] + let _ = tokio::time::timeout(Duration::from_millis(15), notifier_clone.notified()).await; + + // If we're using WASM, we can't use tokio's timeout due to lack of time library in WASM. + // I'm also too lazy to make this a select. So, this'll do. We can't even access this + // protocol in a web context yet since there's no WebHID comm manager yet. + #[cfg(feature = "wasm")] + async_manager::sleep(Duration::from_millis(15)).await; + } + }, + info_span!("NintendoJoyconControlLoop").or_current(), + ); Self { //packet_number: Arc::new(AtomicU8::new(0)), speed_val, diff --git a/crates/buttplug_server/src/device/protocol_impl/patoo.rs b/crates/buttplug_server/src/device/protocol_impl/patoo.rs index 083f41a3c..2975cdc88 100644 --- a/crates/buttplug_server/src/device/protocol_impl/patoo.rs +++ b/crates/buttplug_server/src/device/protocol_impl/patoo.rs @@ -25,18 +25,12 @@ use std::sync::{ use uuid::{Uuid, uuid}; pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct PatooIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; - impl ProtocolIdentifierFactory for PatooIdentifierFactory { - fn identifier(&self) -> &str { - "patoo" - } + pub const IDENTIFIER: &str = "patoo"; - fn create(&self) -> Box { - Box::new(super::PatooIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::PatooIdentifier::default()) } } @@ -59,7 +53,7 @@ impl ProtocolIdentifier for PatooIdentifier { } let name: String = c[0..i].iter().collect(); Ok(( - UserDeviceIdentifier::new(hardware.address(), "Patoo", &Some(name)), + UserDeviceIdentifier::new(hardware.address(), "Patoo", Some(&name)), Box::new(PatooInitializer::default()), )) } diff --git a/crates/buttplug_server/src/device/protocol_impl/prettylove.rs b/crates/buttplug_server/src/device/protocol_impl/prettylove.rs index 53e7de8b3..086c97d86 100644 --- a/crates/buttplug_server/src/device/protocol_impl/prettylove.rs +++ b/crates/buttplug_server/src/device/protocol_impl/prettylove.rs @@ -21,18 +21,12 @@ use std::sync::Arc; use uuid::Uuid; pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct PrettyLoveIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; - impl ProtocolIdentifierFactory for PrettyLoveIdentifierFactory { - fn identifier(&self) -> &str { - "prettylove" - } + pub const IDENTIFIER: &str = "prettylove"; - fn create(&self) -> Box { - Box::new(super::PrettyLoveIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::PrettyLoveIdentifier::default()) } } @@ -50,7 +44,7 @@ impl ProtocolIdentifier for PrettyLoveIdentifier { UserDeviceIdentifier::new( hardware.address(), "prettylove", - &Some("Aogu BLE".to_owned()), + Some("Aogu BLE"), ), Box::new(PrettyLoveInitializer::default()), )) diff --git a/crates/buttplug_server/src/device/protocol_impl/satisfyer.rs b/crates/buttplug_server/src/device/protocol_impl/satisfyer.rs index ebb5904ee..0c198ff25 100644 --- a/crates/buttplug_server/src/device/protocol_impl/satisfyer.rs +++ b/crates/buttplug_server/src/device/protocol_impl/satisfyer.rs @@ -29,18 +29,12 @@ use uuid::{Uuid, uuid}; const SATISFYER_PROTOCOL_UUID: Uuid = uuid!("79a0ed0d-f392-4c48-967e-f4467438c344"); pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct SatisfyerIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; - impl ProtocolIdentifierFactory for SatisfyerIdentifierFactory { - fn identifier(&self) -> &str { - "satisfyer" - } + pub const IDENTIFIER: &str = "satisfyer"; - fn create(&self) -> Box { - Box::new(super::SatisfyerIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::SatisfyerIdentifier::default()) } } @@ -67,7 +61,7 @@ impl ProtocolIdentifier for SatisfyerIdentifier { ); return Ok(( - UserDeviceIdentifier::new(hardware.address(), "satisfyer", &Some(device_identifier)), + UserDeviceIdentifier::new(hardware.address(), "satisfyer", Some(&device_identifier)), Box::new(SatisfyerInitializer::default()), )); } @@ -92,7 +86,7 @@ impl ProtocolIdentifier for SatisfyerIdentifier { device_identifier ); return Ok(( - UserDeviceIdentifier::new(hardware.address(), "satisfyer", &Some(device_identifier)), + UserDeviceIdentifier::new(hardware.address(), "satisfyer", Some(&device_identifier)), Box::new(SatisfyerInitializer::default()), )); } diff --git a/crates/buttplug_server/src/device/protocol_impl/thehandy_v3/mod.rs b/crates/buttplug_server/src/device/protocol_impl/thehandy_v3/mod.rs index d331ebee9..c009963a9 100644 --- a/crates/buttplug_server/src/device/protocol_impl/thehandy_v3/mod.rs +++ b/crates/buttplug_server/src/device/protocol_impl/thehandy_v3/mod.rs @@ -31,19 +31,12 @@ const THEHANDY_V3_PROTOCOL_UUID: Uuid = uuid!("f148e5a6-91fe-4666-944f-2fcec6284 const THEHANDY_V3_SUBSCRIBE_UUID: Uuid = uuid!("55392f4a-7dcb-435f-a33d-38ec4c1d7d7e"); pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; + use crate::device::protocol::ProtocolIdentifier; - #[derive(Default)] - pub struct TheHandyV3IdentifierFactory {} + pub const IDENTIFIER: &str = "thehandy-v3"; - impl ProtocolIdentifierFactory for TheHandyV3IdentifierFactory { - fn identifier(&self) -> &str { - "thehandy-v3" - } - - fn create(&self) -> Box { - Box::new(super::TheHandyV3Identifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::TheHandyV3Identifier::default()) } } @@ -60,7 +53,7 @@ impl ProtocolIdentifier for TheHandyV3Identifier { let bits: Vec<&str> = hardware.name().split('_').collect(); let name = if bits.len() > 2 { bits[1] } else { "unknown" }; Ok(( - UserDeviceIdentifier::new(hardware.address(), "thehandy-v3", &Some(name.to_owned())), + UserDeviceIdentifier::new(hardware.address(), "thehandy-v3", Some(name)), Box::new(TheHandyV3Initializer::default()), )) } diff --git a/crates/buttplug_server/src/device/protocol_impl/vibcrafter.rs b/crates/buttplug_server/src/device/protocol_impl/vibcrafter.rs index 2d797d2f3..ae18a8ceb 100644 --- a/crates/buttplug_server/src/device/protocol_impl/vibcrafter.rs +++ b/crates/buttplug_server/src/device/protocol_impl/vibcrafter.rs @@ -33,7 +33,6 @@ use uuid::{Uuid, uuid}; use rand::distributions::Alphanumeric; use rand::{Rng, thread_rng}; -use regex::Regex; use sha2::{Digest, Sha256}; type Aes128EcbEnc = ecb::Encryptor; @@ -101,31 +100,21 @@ impl ProtocolInitializer for VibCrafterInitializer { debug!("VibCrafter authenticated!"); return Ok(Arc::new(VibCrafter::default())); } - let challenge = Regex::new(r"^[a-zA-Z0-9]{4}:([a-zA-Z0-9]+);$") - .expect("This is static and should always compile"); - if let Some(parts) = challenge.captures(decoded.as_str()) { - debug!("VibCrafter challenge {:?}", parts); - if let Some(to_hash) = parts.get(1) { - debug!("VibCrafter to hash {:?}", to_hash); - let mut sha256 = Sha256::new(); - sha256.update(to_hash.as_str().as_bytes()); - let result = &sha256.finalize(); - - let auth_msg = format!("Auth:{:02x}{:02x};", result[0], result[1]); - hardware - .write_value(&HardwareWriteCmd::new( - &[VIBCRAFTER_PROTOCOL_UUID], - Endpoint::Tx, - encrypt(auth_msg), - false, - )) - .await?; - } else { - return Err(ButtplugDeviceError::ProtocolSpecificError( - "VibCrafter".to_owned(), - "VibCrafter didn't provide a valid security handshake".to_owned(), - )); - } + if decoded.len() > 5 && decoded.ends_with(';') && decoded.as_bytes()[4] == b':' { + let to_hash = &decoded[5..decoded.len() - 1]; + let mut sha256 = Sha256::new(); + sha256.update(to_hash.as_bytes()); + let result = &sha256.finalize(); + + let auth_msg = format!("Auth:{:02x}{:02x};", result[0], result[1]); + hardware + .write_value(&HardwareWriteCmd::new( + &[VIBCRAFTER_PROTOCOL_UUID], + Endpoint::Tx, + encrypt(auth_msg), + false, + )) + .await?; } else { return Err(ButtplugDeviceError::ProtocolSpecificError( "VibCrafter".to_owned(), diff --git a/crates/buttplug_server/src/device/protocol_impl/vibratissimo.rs b/crates/buttplug_server/src/device/protocol_impl/vibratissimo.rs index 2f534c957..7d0a5c47e 100644 --- a/crates/buttplug_server/src/device/protocol_impl/vibratissimo.rs +++ b/crates/buttplug_server/src/device/protocol_impl/vibratissimo.rs @@ -26,18 +26,12 @@ use uuid::{Uuid, uuid}; const VIBRATISSIMO_PROTOCOL_UUID: Uuid = uuid!("66ef7aa4-1e6a-4067-9066-dcb53c7647f2"); pub mod setup { - use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; - #[derive(Default)] - pub struct VibratissimoIdentifierFactory {} + use crate::device::protocol::ProtocolIdentifier; - impl ProtocolIdentifierFactory for VibratissimoIdentifierFactory { - fn identifier(&self) -> &str { - "vibratissimo" - } + pub const IDENTIFIER: &str = "vibratissimo"; - fn create(&self) -> Box { - Box::new(super::VibratissimoIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::VibratissimoIdentifier::default()) } } @@ -62,7 +56,7 @@ impl ProtocolIdentifier for VibratissimoIdentifier { let ident = String::from_utf8(result.data().to_vec()).unwrap_or_else(|_| hardware.name().to_owned()); Ok(( - UserDeviceIdentifier::new(hardware.address(), "vibratissimo", &Some(ident)), + UserDeviceIdentifier::new(hardware.address(), "vibratissimo", Some(&ident)), Box::new(VibratissimoInitializer::default()), )) } diff --git a/crates/buttplug_server/src/device/protocol_impl/xuanhuan.rs b/crates/buttplug_server/src/device/protocol_impl/xuanhuan.rs index 65834fa99..6dfe2932d 100644 --- a/crates/buttplug_server/src/device/protocol_impl/xuanhuan.rs +++ b/crates/buttplug_server/src/device/protocol_impl/xuanhuan.rs @@ -15,10 +15,7 @@ use crate::device::{ }, }; use async_trait::async_trait; -use buttplug_core::{ - errors::ButtplugDeviceError, - util::{async_manager, sleep}, -}; +use buttplug_core::{errors::ButtplugDeviceError, util::async_manager}; use buttplug_server_device_config::{ Endpoint, ProtocolCommunicationSpecifier, @@ -32,6 +29,7 @@ use std::{ }, time::Duration, }; +use tracing::info_span; use uuid::{Uuid, uuid}; const XUANHUAN_PROTOCOL_ID: Uuid = uuid!("e9f9f8ab-4fd5-4573-a4ec-ab542568849b"); @@ -70,7 +68,7 @@ async fn vibration_update_handler(device: Arc, command_holder: Arc &str { - "youou" - } + pub const IDENTIFIER: &str = "youou"; - fn create(&self) -> Box { - Box::new(super::YououIdentifier::default()) - } + pub fn create_identifier() -> Box { + Box::new(super::YououIdentifier::default()) } } @@ -51,7 +45,7 @@ impl ProtocolIdentifier for YououIdentifier { _: ProtocolCommunicationSpecifier, ) -> Result<(UserDeviceIdentifier, Box), ButtplugDeviceError> { Ok(( - UserDeviceIdentifier::new(hardware.address(), "Youou", &Some("VX001_".to_owned())), + UserDeviceIdentifier::new(hardware.address(), "Youou", Some("VX001_")), Box::new(YououInitializer::default()), )) } diff --git a/crates/buttplug_server/src/device/server_device_manager.rs b/crates/buttplug_server/src/device/server_device_manager.rs index c065ef6c1..2d48f4c94 100644 --- a/crates/buttplug_server/src/device/server_device_manager.rs +++ b/crates/buttplug_server/src/device/server_device_manager.rs @@ -38,14 +38,16 @@ use buttplug_core::{ util::{async_manager, stream::convert_broadcast_receiver_to_stream}, }; use buttplug_server_device_config::{DeviceConfigurationManager, UserDeviceIdentifier}; +use compact_str::CompactString; use dashmap::DashMap; use futures::{ Stream, future::{self, FutureExt}, }; use getset::Getters; +use litemap::LiteMap; +use tracing::info_span; use std::{ - collections::BTreeMap, convert::TryFrom, sync::{ Arc, @@ -65,7 +67,7 @@ pub(super) enum DeviceManagerCommand { #[getset(get = "pub")] pub struct ServerDeviceInfo { identifier: UserDeviceIdentifier, - display_name: Option, + display_name: Option, } pub struct ServerDeviceManagerBuilder { @@ -160,7 +162,7 @@ impl ServerDeviceManagerBuilder { ); async_manager::spawn(async move { event_loop.run().await; - }); + }, info_span!("ServerDeviceManagerEventLoop").or_current()); Ok(ServerDeviceManager { device_configuration_manager: self.device_configuration_manager.clone(), devices, @@ -296,7 +298,7 @@ impl ServerDeviceManager { } } - pub(crate) fn feature_map(&self) -> BTreeMap { + pub(crate) fn feature_map(&self) -> LiteMap { self .devices() .iter() diff --git a/crates/buttplug_server/src/device/server_device_manager_event_loop.rs b/crates/buttplug_server/src/device/server_device_manager_event_loop.rs index 75430d79a..d32f54c02 100644 --- a/crates/buttplug_server/src/device/server_device_manager_event_loop.rs +++ b/crates/buttplug_server/src/device/server_device_manager_event_loop.rs @@ -16,15 +16,13 @@ use crate::device::{ DeviceHandle, InternalDeviceEvent, device_handle::build_device_handle, - hardware::communication::{HardwareCommunicationManager, HardwareCommunicationManagerEvent}, - protocol::ProtocolManager, + hardware::communication::{HardwareCommunicationManager, HardwareCommunicationManagerEvent}, protocol::get_protocol_specializers, }; use dashmap::{DashMap, DashSet}; use futures::{FutureExt, future}; use std::sync::Arc; use tokio::sync::{broadcast, mpsc}; use tokio_util::sync::CancellationToken; -use tracing_futures::Instrument; use super::server_device_manager::DeviceManagerCommand; @@ -67,8 +65,6 @@ pub(super) struct ServerDeviceManagerEventLoop { connecting_devices: Arc>, /// Cancellation token for the event loop loop_cancellation_token: CancellationToken, - /// Protocol map, for mapping user definitions to protocols - protocol_manager: ProtocolManager, } impl ServerDeviceManagerEventLoop { @@ -94,7 +90,6 @@ impl ServerDeviceManagerEventLoop { scanning_state: ScanningState::Idle, connecting_devices: Arc::new(DashSet::new()), loop_cancellation_token, - protocol_manager: ProtocolManager::default(), } } @@ -185,9 +180,7 @@ impl ServerDeviceManagerEventLoop { } ScanningState::BringupInProgress => { // Comm manager finished before we completed bringup - ignore for now - debug!( - "Hardware Comm Manager finished before scanning was fully started, ignoring" - ); + debug!("Hardware Comm Manager finished before scanning was fully started, ignoring"); } ScanningState::Active => { // Check if all hardware has actually stopped @@ -248,7 +241,7 @@ impl ServerDeviceManagerEventLoop { // // We used to do this in build_server_device, but we shouldn't mark devices as actually // connecting until after this happens, so we're moving it back here. - let protocol_specializers = self.protocol_manager.protocol_specializers( + let protocol_specializers = get_protocol_specializers( &creator.specifier(), self.device_config_manager.base_communication_specifiers(), self.device_config_manager.user_communication_specifiers(), @@ -291,27 +284,35 @@ impl ServerDeviceManagerEventLoop { // Clone sender again for the forwarding task that build_device_handle will spawn let device_event_sender_for_forwarding = self.device_event_sender.clone(); - async_manager::spawn(async move { - match build_device_handle( - device_config_manager, - creator, - protocol_specializers, - device_event_sender_for_forwarding, - ).await { - Ok(device_handle) => { - if device_event_sender_clone - .send(InternalDeviceEvent::Connected(device_handle)) - .await - .is_err() { - error!("Device manager disappeared before connection established, device will be dropped."); + async_manager::spawn( + async move { + match build_device_handle( + device_config_manager, + creator, + protocol_specializers, + device_event_sender_for_forwarding, + ) + .await + { + Ok(device_handle) => { + if device_event_sender_clone + .send(InternalDeviceEvent::Connected(device_handle)) + .await + .is_err() + { + error!( + "Device manager disappeared before connection established, device will be dropped." + ); + } + } + Err(e) => { + error!("Device errored while trying to connect: {:?}", e); } - }, - Err(e) => { - error!("Device errored while trying to connect: {:?}", e); } - } - connecting_devices.remove(&address); - }.instrument(span)); + connecting_devices.remove(&address); + }, + span.or_current(), + ); } } } @@ -364,7 +365,11 @@ impl ServerDeviceManagerEventLoop { // Note: The device event forwarding task is now spawned in build_device_handle(), // so we no longer need to create it here. - info!("Assigning index {} to {}", device_index, device_handle.name()); + info!( + "Assigning index {} to {}", + device_index, + device_handle.name() + ); self.device_map.insert(device_index, device_handle.clone()); let device_update_message: ButtplugServerMessageV4 = self.generate_device_list().into(); diff --git a/crates/buttplug_server/src/message/serializer/mod.rs b/crates/buttplug_server/src/message/serializer/mod.rs index 359854c67..0d278f9c7 100644 --- a/crates/buttplug_server/src/message/serializer/mod.rs +++ b/crates/buttplug_server/src/message/serializer/mod.rs @@ -5,6 +5,8 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. +use std::sync::OnceLock; + use buttplug_core::{ errors::{ButtplugError, ButtplugHandshakeError, ButtplugMessageError}, message::{ @@ -28,7 +30,6 @@ use buttplug_core::{ }, }; use jsonschema::Validator; -use once_cell::sync::OnceCell; use serde::Deserialize; use super::{ @@ -69,14 +70,14 @@ impl ButtplugMessageFinalizer for RequestServerInfoVersion { } pub struct ButtplugServerJSONSerializer { - pub(super) message_version: OnceCell, + pub(super) message_version: OnceLock, validator: Validator, } impl Default for ButtplugServerJSONSerializer { fn default() -> Self { Self { - message_version: OnceCell::new(), + message_version: OnceLock::new(), validator: create_message_validator(), } } diff --git a/crates/buttplug_server/src/message/server_device_attributes.rs b/crates/buttplug_server/src/message/server_device_attributes.rs index 014f7326c..655da6ffd 100644 --- a/crates/buttplug_server/src/message/server_device_attributes.rs +++ b/crates/buttplug_server/src/message/server_device_attributes.rs @@ -11,7 +11,7 @@ use super::v2::ServerDeviceMessageAttributesV2; use buttplug_core::errors::ButtplugError; use buttplug_server_device_config::ServerDeviceFeature; use getset::Getters; -use std::collections::BTreeMap; +use litemap::LiteMap; #[derive(Debug, Getters, Clone)] pub(crate) struct ServerDeviceAttributes { @@ -23,11 +23,11 @@ pub(crate) struct ServerDeviceAttributes { #[getset(get = "pub")] attrs_v3: ServerDeviceMessageAttributesV3, #[getset(get = "pub")] - features: BTreeMap, + features: LiteMap, } impl ServerDeviceAttributes { - pub fn new(features: &BTreeMap) -> Self { + pub fn new(features: &LiteMap) -> Self { let f: Vec = features.values().cloned().collect(); Self { attrs_v3: ServerDeviceMessageAttributesV3::from(f.clone()), @@ -46,6 +46,6 @@ where { fn try_from_client_message( msg: T, - features: &BTreeMap, + features: &LiteMap, ) -> Result; } diff --git a/crates/buttplug_server/src/message/v3/client_device_message_attributes.rs b/crates/buttplug_server/src/message/v3/client_device_message_attributes.rs index f09546ee0..ed55b645e 100644 --- a/crates/buttplug_server/src/message/v3/client_device_message_attributes.rs +++ b/crates/buttplug_server/src/message/v3/client_device_message_attributes.rs @@ -9,16 +9,18 @@ use crate::message::{ v1::NullDeviceMessageAttributesV1, v2::{ClientDeviceMessageAttributesV2, GenericDeviceMessageAttributesV2}, }; -use buttplug_core::message::{ - DeviceFeature, - DeviceFeatureOutputValueProperties, - InputCommandType, - InputType, - OutputType, +use buttplug_core::{ + message::{ + DeviceFeature, + DeviceFeatureOutputValueProperties, + InputCommandType, + InputType, + OutputType, + }, + util::range::RangeInclusive, }; use getset::{Getters, MutGetters, Setters}; -use serde::{Deserialize, Serialize, Serializer, ser::SerializeSeq}; -use std::ops::RangeInclusive; +use serde::{Deserialize, Serialize}; // This will look almost exactly like ServerDeviceMessageAttributes. However, it will only contain // information we want the client to know, i.e. step counts versus specific step ranges. This is @@ -201,20 +203,6 @@ impl ClientGenericDeviceMessageAttributesV3 { } } -fn range_sequence_serialize( - range_vec: &Vec>, - serializer: S, -) -> Result -where - S: Serializer, -{ - let mut seq = serializer.serialize_seq(Some(range_vec.len()))?; - for range in range_vec { - seq.serialize_element(&vec![*range.start(), *range.end()])?; - } - seq.end() -} - #[derive(Clone, Debug, Serialize, Deserialize, Getters, Setters)] pub struct SensorDeviceMessageAttributesV3 { #[getset(get = "pub")] @@ -224,7 +212,7 @@ pub struct SensorDeviceMessageAttributesV3 { #[serde(rename = "SensorType")] pub(in crate::message) sensor_type: InputType, #[getset(get = "pub")] - #[serde(rename = "SensorRange", serialize_with = "range_sequence_serialize")] + #[serde(rename = "SensorRange")] pub(in crate::message) sensor_range: Vec>, // TODO This needs to actually be part of the device info relayed to the client in spec v4. #[getset(get = "pub")] @@ -294,7 +282,7 @@ impl From> for ClientDeviceMessageAttributesV3 { let mut actuator_vec = vec![]; if let Some(output_map) = feature.output() && let Some(actuator) = output_map.rotate() - && *actuator.value().start() < 0 + && actuator.value().start() < 0 { let actuator_type = OutputType::Rotate; let attrs = ClientGenericDeviceMessageAttributesV3 { @@ -338,7 +326,7 @@ impl From> for ClientDeviceMessageAttributesV3 { // Only convert Battery backwards. Other sensors weren't really built for v3 and we // never recommended using them or implemented much for them. if let Some(battery) = sensor_map.battery() - && battery.command().contains(&InputCommandType::Read) + && battery.command().contains(InputCommandType::Read) { sensor_vec.push(SensorDeviceMessageAttributesV3 { feature_descriptor: feature.description().to_owned(), diff --git a/crates/buttplug_server/src/message/v3/server_device_message_attributes.rs b/crates/buttplug_server/src/message/v3/server_device_message_attributes.rs index 3632f2784..f2e98080f 100644 --- a/crates/buttplug_server/src/message/v3/server_device_message_attributes.rs +++ b/crates/buttplug_server/src/message/v3/server_device_message_attributes.rs @@ -6,11 +6,10 @@ // for full license information. use crate::message::v1::NullDeviceMessageAttributesV1; -use buttplug_core::message::{InputType, OutputType}; +use buttplug_core::{message::{InputType, OutputType}, util::range::RangeInclusive}; use buttplug_server_device_config::ServerDeviceFeature; use getset::{Getters, MutGetters, Setters}; -use std::ops::RangeInclusive; #[derive(Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters)] #[getset(get = "pub")] @@ -62,7 +61,7 @@ impl From> for ServerDeviceMessageAttributesV3 { let mut create_attribute = |actuator_type, step_count| { let actuator_type = actuator_type; let attrs = ServerGenericDeviceMessageAttributesV3 { - feature_descriptor: feature.description().to_owned(), + feature_descriptor: feature.description().to_string(), actuator_type, step_count, feature: feature.clone(), @@ -109,12 +108,12 @@ impl From> for ServerDeviceMessageAttributesV3 { let mut actuator_vec = vec![]; if let Some(output_map) = feature.output() && let Some(actuator) = output_map.rotate() - && *actuator.value().base().start() < 0 + && actuator.value().base().start() < 0 { let actuator_type = OutputType::Rotate; let step_count = actuator.value().step_count(); let attrs = ServerGenericDeviceMessageAttributesV3 { - feature_descriptor: feature.description().to_owned(), + feature_descriptor: feature.description().to_string(), actuator_type, step_count, feature: feature.clone(), @@ -136,7 +135,7 @@ impl From> for ServerDeviceMessageAttributesV3 { let actuator_type = OutputType::Position; let step_count = actuator.value().step_count(); let attrs = ServerGenericDeviceMessageAttributesV3 { - feature_descriptor: feature.description().to_owned(), + feature_descriptor: feature.description().to_string(), actuator_type, step_count, feature: feature.clone(), @@ -159,7 +158,7 @@ impl From> for ServerDeviceMessageAttributesV3 { // Only convert Battery backwards. Other sensors weren't really built for v3 and we // never recommended using them or implemented much for them. sensor_vec.push(ServerSensorDeviceMessageAttributesV3 { - feature_descriptor: feature.description().to_owned(), + feature_descriptor: feature.description().to_string(), sensor_type: InputType::Battery, sensor_range: battery.value().clone(), feature: feature.clone(), diff --git a/crates/buttplug_server/src/message/v4/spec_enums.rs b/crates/buttplug_server/src/message/v4/spec_enums.rs index 4f649bb85..74d489af9 100644 --- a/crates/buttplug_server/src/message/v4/spec_enums.rs +++ b/crates/buttplug_server/src/message/v4/spec_enums.rs @@ -5,7 +5,7 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use std::{collections::BTreeMap, fmt::Debug, u32}; +use std::{fmt::Debug, u32}; use crate::message::{ ButtplugClientMessageVariant, @@ -23,9 +23,18 @@ use crate::message::{ use buttplug_core::{ errors::{ButtplugDeviceError, ButtplugError, ButtplugMessageError}, message::{ - ButtplugClientMessageV4, ButtplugDeviceMessage, ButtplugMessage, PingV0, RequestDeviceListV0, RequestServerInfoV4, StartScanningV0, StopCmdV4, StopScanningV0 + ButtplugClientMessageV4, + ButtplugDeviceMessage, + ButtplugMessage, + PingV0, + RequestDeviceListV0, + RequestServerInfoV4, + StartScanningV0, + StopCmdV4, + StopScanningV0, }, }; +use litemap::LiteMap; use super::{ checked_input_cmd::CheckedInputCmdV4, @@ -74,7 +83,7 @@ impl_message_enum_traits!(ButtplugCheckedClientMessageV4 { impl TryFromClientMessage for ButtplugCheckedClientMessageV4 { fn try_from_client_message( value: ButtplugClientMessageV4, - feature_map: &BTreeMap, + feature_map: &LiteMap, ) -> Result { match value { // Messages that don't need checking @@ -93,7 +102,10 @@ impl TryFromClientMessage for ButtplugCheckedClientMess } // Messages that need device index checking ButtplugClientMessageV4::StopCmd(m) => { - if m.device_index().map_or(true,|x| feature_map.get(&x).is_some()) { + if m + .device_index() + .map_or(true, |x| feature_map.get(&x).is_some()) + { Ok(ButtplugCheckedClientMessageV4::StopCmd(m)) } else { Err(ButtplugError::from( @@ -186,7 +198,7 @@ impl TryFrom for ButtplugCheckedClientMessageV4 { impl TryFromClientMessage for ButtplugCheckedClientMessageV4 { fn try_from_client_message( msg: ButtplugClientMessageVariant, - features: &BTreeMap, + features: &LiteMap, ) -> Result { let id = msg.id(); let mut converted_msg = match msg { @@ -205,7 +217,7 @@ impl TryFromClientMessage for ButtplugCheckedClien impl TryFromClientMessage for ButtplugCheckedClientMessageV4 { fn try_from_client_message( msg: ButtplugClientMessageV0, - features: &BTreeMap, + features: &LiteMap, ) -> Result { // All v0 messages can be converted to v1 messages. Self::try_from_client_message(ButtplugClientMessageV1::from(msg), features) @@ -214,7 +226,7 @@ impl TryFromClientMessage for ButtplugCheckedClientMess fn check_device_index_and_convert( msg: T, - features: &BTreeMap, + features: &LiteMap, ) -> Result where T: ButtplugDeviceMessage + Debug, @@ -233,7 +245,7 @@ where impl TryFromClientMessage for ButtplugCheckedClientMessageV4 { fn try_from_client_message( msg: ButtplugClientMessageV1, - features: &BTreeMap, + features: &LiteMap, ) -> Result { // Instead of converting to v2 message attributes then to v4 device features, we move directly // from v0 command messages to v4 device features here. There's no reason to do the middle step. @@ -253,7 +265,7 @@ impl TryFromClientMessage for ButtplugCheckedClientMess impl TryFromClientMessage for ButtplugCheckedClientMessageV4 { fn try_from_client_message( msg: ButtplugClientMessageV2, - features: &BTreeMap, + features: &LiteMap, ) -> Result { match msg { // Convert v2 specific queries to v3 generic sensor queries @@ -272,7 +284,7 @@ impl TryFromClientMessage for ButtplugCheckedClientMess impl TryFromClientMessage for ButtplugCheckedClientMessageV4 { fn try_from_client_message( msg: ButtplugClientMessageV3, - features: &BTreeMap, + features: &LiteMap, ) -> Result { match msg { // Convert v1/v2 message attribute commands into device feature commands diff --git a/crates/buttplug_server/src/ping_timer.rs b/crates/buttplug_server/src/ping_timer.rs index fdf2c9489..9a7b3f7bd 100644 --- a/crates/buttplug_server/src/ping_timer.rs +++ b/crates/buttplug_server/src/ping_timer.rs @@ -5,13 +5,14 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use buttplug_core::util::{async_manager, sleep}; +use buttplug_core::util::async_manager; use futures::Future; use std::{sync::Arc, time::Duration}; use tokio::{ select, sync::{Mutex, mpsc}, }; +use tracing::info_span; pub enum PingMessage { Ping, @@ -33,7 +34,7 @@ async fn ping_timer( let mut pinged = false; loop { select! { - _ = sleep(Duration::from_millis(max_ping_time.into())) => { + _ = async_manager::sleep(Duration::from_millis(max_ping_time.into())) => { if started { if !pinged { // Ping timeout occurred - call the callback @@ -70,11 +71,14 @@ impl Drop for PingTimer { // This cannot block, otherwise it will throw in WASM contexts on // destruction. We must use send(), not blocking_send(). let sender = self.ping_msg_sender.clone(); - async_manager::spawn(async move { - if sender.send(PingMessage::End).await.is_err() { - debug!("Receiver does not exist, assuming ping timer event loop already dead."); - } - }); + async_manager::spawn( + async move { + if sender.send(PingMessage::End).await.is_err() { + debug!("Receiver does not exist, assuming ping timer event loop already dead."); + } + }, + info_span!("PingTimer::drop").or_current(), + ); } } @@ -92,7 +96,7 @@ impl PingTimer { if max_ping_time > 0 { let callback = Arc::new(Mutex::new(on_ping_timeout)); let fut = ping_timer(max_ping_time, receiver, callback); - async_manager::spawn(fut); + async_manager::spawn(fut, info_span!("ping_timer").or_current()); } Self { max_ping_time, diff --git a/crates/buttplug_server/src/server.rs b/crates/buttplug_server/src/server.rs index 3debf7ef6..d41a9bacb 100644 --- a/crates/buttplug_server/src/server.rs +++ b/crates/buttplug_server/src/server.rs @@ -43,15 +43,11 @@ use futures::{ }; use std::{ fmt, - sync::{ - Arc, - RwLock, - }, + sync::{Arc, RwLock}, }; use tokio::sync::broadcast; use tokio_stream::StreamExt; -use tracing::info_span; -use tracing_futures::Instrument; +use tracing::{Instrument, info_span}; /// Connection state for the ButtplugServer. /// Replaces separate connected/client_name/spec_version fields with explicit states. @@ -215,9 +211,8 @@ impl ButtplugServer { let stop_scanning_fut = self.parse_checked_message( ButtplugCheckedClientMessageV4::StopScanning(StopScanningV0::default()), ); - let stop_fut = self.parse_checked_message(ButtplugCheckedClientMessageV4::StopCmd( - StopCmdV4::default(), - )); + let stop_fut = + self.parse_checked_message(ButtplugCheckedClientMessageV4::StopCmd(StopCmdV4::default())); let state = self.state.clone(); async move { { @@ -255,7 +250,7 @@ impl ButtplugServer { current_version } else { info!("Setting Buttplug Server Message Spec version to {}", v); - v + v }; match msg { ButtplugClientMessageVariant::V4(msg) => { diff --git a/crates/buttplug_server/src/server_builder.rs b/crates/buttplug_server/src/server_builder.rs index d0a3ff65a..4f282e23a 100644 --- a/crates/buttplug_server/src/server_builder.rs +++ b/crates/buttplug_server/src/server_builder.rs @@ -19,6 +19,7 @@ use buttplug_core::{ use buttplug_server_device_config::DeviceConfigurationManagerBuilder; use std::sync::{Arc, RwLock}; use tokio::sync::broadcast; +use tracing::info_span; /// Configures and creates [ButtplugServer] instances. pub struct ButtplugServerBuilder { @@ -114,14 +115,17 @@ impl ButtplugServerBuilder { *state_guard = ConnectionState::PingedOut; } // Stop all devices (spawn async task since callback is sync) - async_manager::spawn(async move { - if let Err(e) = device_manager_clone - .stop_devices(&StopCmdV4::default()) - .await - { - error!("Could not stop devices on ping timeout: {:?}", e); - } - }); + async_manager::spawn( + async move { + if let Err(e) = device_manager_clone + .stop_devices(&StopCmdV4::default()) + .await + { + error!("Could not stop devices on ping timeout: {:?}", e); + } + }, + info_span!("ButtplugServerBuilder::ping_timeout_callback stop_devices").or_current(), + ); // Send error to output channel if output_sender_clone .send(ButtplugServerMessageV4::Error(message::ErrorV0::from( diff --git a/crates/buttplug_server_device_config/Cargo.toml b/crates/buttplug_server_device_config/Cargo.toml index 5c6c41469..6cbb74a22 100644 --- a/crates/buttplug_server_device_config/Cargo.toml +++ b/crates/buttplug_server_device_config/Cargo.toml @@ -19,7 +19,7 @@ doctest = true doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } futures = "0.3.31" futures-util = "0.3.31" serde = { version = "1.0.228", features = ["derive"] } @@ -34,12 +34,15 @@ jsonschema = { version = "0.38.1", default-features = false } uuid = { version = "1.20.0", features = ["serde", "v4"] } strum_macros = "0.27.2" strum = "0.27.2" +litemap = { version = "0.8.1", features = ["serde"] } +compact_str = { version = "0.9.0", features = ["serde"] } +enumflags2 = "0.7.12" [build-dependencies] serde_yaml = "0.9.34" serde_json = "1.0.149" serde = { version = "1.0.228", features = ["derive"] } -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } [dev-dependencies] test-case = "3.3.1" diff --git a/crates/buttplug_server_device_config/build.rs b/crates/buttplug_server_device_config/build.rs index df558f20a..4b13ff304 100644 --- a/crates/buttplug_server_device_config/build.rs +++ b/crates/buttplug_server_device_config/build.rs @@ -39,6 +39,7 @@ fn main() { // Open version file let mut version: VersionFile = serde_yaml::from_str(&std::fs::read_to_string(VERSION_FILE).unwrap()).unwrap(); + save_rust_version_file(version.version); // Bump minor version version.version.minor += 1; @@ -86,4 +87,15 @@ fn main() { ) .unwrap(); std::fs::write(OUTPUT_FILE, json.as_bytes()).unwrap(); + save_rust_version_file(version.version); +} + +fn save_rust_version_file(version: BuildVersion) { + std::fs::write( + format!("{}/version.rs", std::env::var("OUT_DIR").unwrap()), + format!( + "fn get_internal_config_version() -> ConfigVersion {{ ConfigVersion {{ major: {}, minor: {} }} }}", + version.major, version.minor + ), + ).unwrap(); } diff --git a/crates/buttplug_server_device_config/src/device_config_file/base.rs b/crates/buttplug_server_device_config/src/device_config_file/base.rs index df0a780f4..8a635cd2f 100644 --- a/crates/buttplug_server_device_config/src/device_config_file/base.rs +++ b/crates/buttplug_server_device_config/src/device_config_file/base.rs @@ -7,7 +7,8 @@ use std::collections::HashMap; -use getset::Getters; +use compact_str::CompactString; +use getset::{Getters, MutGetters}; use serde::{Deserialize, Serialize}; use crate::device_config_file::{ @@ -17,14 +18,14 @@ use crate::device_config_file::{ protocol::ProtocolDefinition, }; -#[derive(Deserialize, Serialize, Debug, Getters)] +#[derive(Deserialize, Serialize, Debug, Getters, MutGetters)] #[getset(get_mut = "pub", set = "pub")] pub struct BaseConfigFile { #[getset(get_copy = "pub")] version: ConfigVersion, #[getset(get = "pub")] #[serde(default, skip_serializing_if = "Option::is_none")] - protocols: Option>, + protocols: Option>, } impl Default for BaseConfigFile { diff --git a/crates/buttplug_server_device_config/src/device_config_file/device.rs b/crates/buttplug_server_device_config/src/device_config_file/device.rs index d1378cc0e..bcfa3bc26 100644 --- a/crates/buttplug_server_device_config/src/device_config_file/device.rs +++ b/crates/buttplug_server_device_config/src/device_config_file/device.rs @@ -5,6 +5,7 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. +use compact_str::CompactString; use getset::{CopyGetters, Getters, MutGetters}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -16,14 +17,14 @@ use super::feature::{ConfigBaseDeviceFeature, ConfigUserDeviceFeature}; #[derive(Debug, Clone, Getters, CopyGetters, Serialize, Deserialize)] pub struct ConfigBaseDeviceDefinition { #[getset(get = "pub")] - identifier: Option>, + pub identifier: Option>, #[getset(get = "pub")] /// Given name of the device this instance represents. - name: String, + name: CompactString, #[getset(get_copy = "pub")] id: Uuid, #[getset(get = "pub")] - protocol_variant: Option, + protocol_variant: Option, #[getset(get_copy = "pub")] message_gap_ms: Option, #[getset(get = "pub")] @@ -31,33 +32,38 @@ pub struct ConfigBaseDeviceDefinition { } impl ConfigBaseDeviceDefinition { - pub fn update_with_configuration(&self, config: ConfigBaseDeviceDefinition) -> Self { + pub fn with_defaults(self, defaults: Option<&ConfigBaseDeviceDefinition>) -> Self { Self { - identifier: config.identifier().clone(), - name: config.name().clone(), - id: config.id(), - protocol_variant: config.protocol_variant.or(self.protocol_variant.clone()), - message_gap_ms: config.message_gap_ms.or(self.message_gap_ms), - features: config.features.or(self.features.clone()), + identifier: self.identifier, + name: self.name, + id: self.id, + protocol_variant: self + .protocol_variant + .or_else(|| defaults.and_then(|d| d.protocol_variant.clone())), + message_gap_ms: self + .message_gap_ms + .or_else(|| defaults.and_then(|d| d.message_gap_ms)), + features: self + .features + .or_else(|| defaults.and_then(|d| d.features.clone())), } } } impl From for ServerDeviceDefinition { fn from(val: ConfigBaseDeviceDefinition) -> Self { - let mut builder = ServerDeviceDefinitionBuilder::new(&val.name, &val.id); - if let Some(variant) = val.protocol_variant { - builder.protocol_variant(&variant); - } - if let Some(gap) = val.message_gap_ms { - builder.message_gap_ms(Some(gap)); - } - if let Some(features) = val.features { - for feature in features { - builder.add_feature(&feature.into()); - } - } - builder.finish() + ServerDeviceDefinitionBuilder::new_with_features( + val.name, + val.id, + val + .features + .unwrap_or_default() + .into_iter() + .map(Into::into) + ) + .protocol_variant(val.protocol_variant) + .message_gap_ms(val.message_gap_ms) + .finish() } } @@ -65,7 +71,7 @@ impl From for ServerDeviceDefinition { pub struct ConfigUserDeviceCustomization { #[serde(default, skip_serializing_if = "Option::is_none")] #[getset(get = "pub")] - display_name: Option, + display_name: Option, #[serde(default)] #[getset(get_copy = "pub")] allow: bool, @@ -111,22 +117,24 @@ impl ConfigUserDeviceDefinition { &self, base: &ServerDeviceDefinition, ) -> Result { - let mut builder = ServerDeviceDefinitionBuilder::from_base(base, self.id, false); - builder.display_name(&self.user_config.display_name); - builder.message_gap_ms(self.user_config.message_gap_ms); - self.user_config.allow.then(|| builder.allow(true)); - self.user_config.deny.then(|| builder.deny(true)); - builder.index(self.user_config.index); if self.features().len() != base.features().len() { return Err(ButtplugDeviceConfigError::UserFeatureMismatch); } + + let mut builder = ServerDeviceDefinitionBuilder::from_base(base, self.id, false) + .display_name(self.user_config.display_name.clone()) + .message_gap_ms(self.user_config.message_gap_ms) + .allow(self.user_config.allow) + .deny(self.user_config.deny) + .index(self.user_config.index); + for feature in self.features() { if let Some(base_feature) = base .features() .values() .find(|x| x.id() == feature.base_id()) { - builder.add_feature(&feature.with_base_feature(base_feature)?); + builder = builder.add_feature(feature.with_base_feature(base_feature)?); } else { return Err(ButtplugDeviceConfigError::UserFeatureMismatch); } @@ -141,7 +149,9 @@ impl TryFrom<&ServerDeviceDefinition> for ConfigUserDeviceDefinition { fn try_from(value: &ServerDeviceDefinition) -> Result { Ok(Self { id: value.id(), - base_id: value.base_id().ok_or(ButtplugDeviceConfigError::MissingBaseId)?, + base_id: value + .base_id() + .ok_or(ButtplugDeviceConfigError::MissingBaseId)?, features: value .features() .values() diff --git a/crates/buttplug_server_device_config/src/device_config_file/feature.rs b/crates/buttplug_server_device_config/src/device_config_file/feature.rs index 0dcbac476..1753c91fa 100644 --- a/crates/buttplug_server_device_config/src/device_config_file/feature.rs +++ b/crates/buttplug_server_device_config/src/device_config_file/feature.rs @@ -5,7 +5,6 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use std::ops::RangeInclusive; use crate::{ ButtplugDeviceConfigError, @@ -17,7 +16,8 @@ use crate::{ ServerDeviceFeatureOutputHwPositionWithDurationProperties, ServerDeviceFeatureOutputValueProperties, }; -use buttplug_core::util::range_serialize::option_range_serialize; +use buttplug_core::util::range::RangeInclusive; +use compact_str::CompactString; use getset::{CopyGetters, Getters}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -39,7 +39,6 @@ impl BaseFeatureSettings { struct UserDeviceFeatureOutputValueProperties { #[serde( skip_serializing_if = "Option::is_none", - serialize_with = "option_range_serialize" )] value: Option>, #[serde(default)] @@ -72,7 +71,6 @@ impl From<&ServerDeviceFeatureOutputValueProperties> for UserDeviceFeatureOutput struct UserDeviceFeatureOutputPositionProperties { #[serde( skip_serializing_if = "Option::is_none", - serialize_with = "option_range_serialize" )] value: Option>, #[serde(default)] @@ -111,12 +109,10 @@ impl From<&ServerDeviceFeatureOutputPositionProperties> struct UserDeviceFeatureOutputHwPositionWithDurationProperties { #[serde( skip_serializing_if = "Option::is_none", - serialize_with = "option_range_serialize" )] value: Option>, #[serde( skip_serializing_if = "Option::is_none", - serialize_with = "option_range_serialize" )] duration: Option>, #[serde(default)] @@ -242,7 +238,7 @@ pub struct ConfigBaseDeviceFeature { index: u32, #[getset(get = "pub")] #[serde(default)] - description: String, + description: CompactString, #[getset(get = "pub")] #[serde(skip_serializing_if = "Option::is_none")] output: Option, @@ -260,12 +256,12 @@ impl From for ServerDeviceFeature { fn from(val: ConfigBaseDeviceFeature) -> Self { ServerDeviceFeature::new( val.index, - &val.description, + val.description, val.id, None, val.feature_settings.alt_protocol_index, - &val.output, - &val.input, + val.output, + val.input, ) } } @@ -297,12 +293,12 @@ impl ConfigUserDeviceFeature { }; Ok(ServerDeviceFeature::new( base_feature.index(), - base_feature.description(), + base_feature.description().clone(), self.id, Some(self.base_id), base_feature.alt_protocol_index(), - &output, - base_feature.input(), + output, + base_feature.input().clone(), )) } } diff --git a/crates/buttplug_server_device_config/src/device_config_file/mod.rs b/crates/buttplug_server_device_config/src/device_config_file/mod.rs index c12c89841..6eb4b8386 100644 --- a/crates/buttplug_server_device_config/src/device_config_file/mod.rs +++ b/crates/buttplug_server_device_config/src/device_config_file/mod.rs @@ -13,9 +13,12 @@ mod user; use base::BaseConfigFile; -use crate::device_config_file::{ - protocol::ProtocolDefinition, - user::{UserConfigDefinition, UserConfigFile, UserDeviceConfigPair}, +use crate::{ + ServerDeviceDefinition, + device_config_file::{ + protocol::ProtocolDefinition, + user::{UserConfigDefinition, UserConfigFile, UserDeviceConfigPair}, + }, }; use super::{BaseDeviceIdentifier, DeviceConfigurationManager, DeviceConfigurationManagerBuilder}; @@ -25,14 +28,16 @@ use buttplug_core::{ }; use dashmap::DashMap; use getset::CopyGetters; -use serde::{Deserialize, Serialize}; -use std::fmt::Display; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use std::{collections::HashMap, fmt::Display, sync::Arc}; pub static DEVICE_CONFIGURATION_JSON: &str = include_str!("../../build-config/buttplug-device-config-v4.json"); static DEVICE_CONFIGURATION_JSON_SCHEMA: &str = include_str!("../../device-config-v4/buttplug-device-config-schema-v4.json"); +include!(concat!(env!("OUT_DIR"), "/version.rs")); + #[derive(Deserialize, Serialize, Debug, CopyGetters, Clone, Copy)] #[getset(get_copy = "pub", get_mut = "pub")] struct ConfigVersion { @@ -50,22 +55,16 @@ trait ConfigVersionGetter { fn version(&self) -> ConfigVersion; } -fn get_internal_config_version() -> ConfigVersion { - let config: BaseConfigFile = serde_json::from_str(DEVICE_CONFIGURATION_JSON) - .expect("If this fails, the whole library goes with it."); - config.version() -} - fn load_protocol_config_from_json<'a, T>( config_str: &'a str, skip_version_check: bool, ) -> Result where - T: ConfigVersionGetter + Deserialize<'a>, + T: ConfigVersionGetter + DeserializeOwned, { let config_validator = JSONValidator::new(DEVICE_CONFIGURATION_JSON_SCHEMA); match config_validator.validate(config_str) { - Ok(_) => match serde_json::from_str::(config_str) { + Ok(value) => match serde_json::from_value::(value) { Ok(protocol_config) => { let internal_config_version = get_internal_config_version(); if !skip_version_check && protocol_config.version().major != internal_config_version.major { @@ -92,53 +91,75 @@ fn load_main_config( main_config_str: &Option, skip_version_check: bool, ) -> Result { - if main_config_str.is_some() { - info!("Loading from custom base device configuration...") - } else { - info!("Loading from internal base device configuration...") - } // Start by loading the main config - let main_config = load_protocol_config_from_json::( - main_config_str - .as_ref() - .unwrap_or(&DEVICE_CONFIGURATION_JSON.to_owned()), - skip_version_check, - )?; + let main_config_str = match main_config_str { + Some(config_str) => { + info!("Loading main configuration from string."); + config_str.as_str() + } + None => { + info!("No main configuration provided, loading from internal config."); + DEVICE_CONFIGURATION_JSON + } + }; + let mut main_config = + load_protocol_config_from_json::(main_config_str, skip_version_check)?; info!("Loaded config version {:?}", main_config.version()); - let mut dcm_builder = DeviceConfigurationManagerBuilder::default(); - - for (protocol_name, protocol_def) in main_config.protocols().clone().unwrap_or_default() { - if let Some(specifiers) = protocol_def.communication() { - dcm_builder.communication_specifier(&protocol_name, specifiers); - } - - let mut default = None; - if let Some(features) = protocol_def.defaults() { - default = Some(features.clone()); - dcm_builder.base_device_definition( - &BaseDeviceIdentifier::new_default(&protocol_name), - &features.clone().into(), - ); - } + let protocols = main_config.protocols_mut().take().unwrap_or_default(); - for config in protocol_def.configurations() { - if let Some(idents) = config.identifier() { + let mut base_communication_specifiers = HashMap::with_capacity(protocols.len()); + let base_device_definitions_capacity = protocols + .iter() + .map(|(_, def)| { + def + .configurations() + .iter() + .map(|config| { + config + .identifier() + .as_ref() + .map_or(0, |idents| idents.len()) + }) + .sum::() + }) + .sum::(); + let mut base_device_definitions = HashMap::with_capacity(base_device_definitions_capacity); + + for (protocol_name, protocol_def) in protocols { + let ProtocolDefinition { + communication, + defaults, + configurations, + } = protocol_def; + + for mut config in configurations { + let identifier = config.identifier.take(); + let definition: Arc = + Arc::new(config.with_defaults(defaults.as_ref()).into()); + if let Some(idents) = identifier { for config_ident in idents { let ident = BaseDeviceIdentifier::new_with_identifier(&protocol_name, config_ident); - if let Some(d) = &default { - dcm_builder - .base_device_definition(&ident, &d.update_with_configuration(config.clone()).into()); - } else { - dcm_builder.base_device_definition(&ident, &config.clone().into()); - } + base_device_definitions.insert(ident, definition.clone()); } } } + + if let Some(defaults) = defaults { + let ident = BaseDeviceIdentifier::new_default(&protocol_name); + base_device_definitions.insert(ident, Arc::new(defaults.into())); + } + + if let Some(specifiers) = communication { + base_communication_specifiers.insert(protocol_name, specifiers); + } } - Ok(dcm_builder) + Ok(DeviceConfigurationManagerBuilder::new( + base_communication_specifiers, + base_device_definitions, + )) } fn load_user_config( @@ -173,7 +194,8 @@ fn load_user_config( for config in protocol_def.configurations() { if let Some(idents) = config.identifier() { for config_ident in idents { - let ident = BaseDeviceIdentifier::new_with_identifier(&protocol_name, config_ident); + let ident = + BaseDeviceIdentifier::new_with_identifier(&protocol_name, config_ident.clone()); dcm_builder.base_device_definition(&ident, &config.clone().into()); } } diff --git a/crates/buttplug_server_device_config/src/device_config_file/user.rs b/crates/buttplug_server_device_config/src/device_config_file/user.rs index 46d556494..40210c5ec 100644 --- a/crates/buttplug_server_device_config/src/device_config_file/user.rs +++ b/crates/buttplug_server_device_config/src/device_config_file/user.rs @@ -5,6 +5,7 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. +use compact_str::CompactString; use dashmap::DashMap; use getset::{Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; @@ -30,7 +31,7 @@ pub struct UserDeviceConfigPair { #[getset(get = "pub", set = "pub", get_mut = "pub")] pub struct UserConfigDefinition { #[serde(skip_serializing_if = "Option::is_none")] - pub protocols: Option>, + pub protocols: Option>, #[serde(rename = "devices", default, skip_serializing_if = "Option::is_none")] pub user_device_configs: Option>, } diff --git a/crates/buttplug_server_device_config/src/device_config_manager.rs b/crates/buttplug_server_device_config/src/device_config_manager.rs index 3be966bb3..ca8086e8f 100644 --- a/crates/buttplug_server_device_config/src/device_config_manager.rs +++ b/crates/buttplug_server_device_config/src/device_config_manager.rs @@ -6,11 +6,13 @@ // for full license information. use buttplug_core::errors::ButtplugDeviceError; +use compact_str::CompactString; use dashmap::DashMap; use getset::Getters; use std::{ collections::HashMap, fmt::{self, Debug}, + sync::Arc, }; use uuid::Uuid; @@ -25,13 +27,24 @@ use crate::{ #[derive(Default, Clone)] pub struct DeviceConfigurationManagerBuilder { - base_communication_specifiers: HashMap>, - user_communication_specifiers: DashMap>, - base_device_definitions: HashMap, + base_communication_specifiers: HashMap>, + user_communication_specifiers: DashMap>, + base_device_definitions: HashMap>, user_device_definitions: DashMap, } impl DeviceConfigurationManagerBuilder { + pub fn new( + base_communication_specifiers: HashMap>, + base_device_definitions: HashMap>, + ) -> Self { + Self { + base_communication_specifiers, + base_device_definitions, + ..Default::default() + } + } + pub fn communication_specifier( &mut self, protocol_name: &str, @@ -39,7 +52,7 @@ impl DeviceConfigurationManagerBuilder { ) -> &mut Self { self .base_communication_specifiers - .entry(protocol_name.to_owned()) + .entry(protocol_name.into()) .or_default() .extend(specifier.iter().cloned()); self @@ -52,7 +65,7 @@ impl DeviceConfigurationManagerBuilder { ) -> &mut Self { self .base_device_definitions - .insert(identifier.clone(), features.clone()); + .insert(identifier.clone(), Arc::new(features.clone())); self } @@ -63,7 +76,7 @@ impl DeviceConfigurationManagerBuilder { ) -> &mut Self { self .user_communication_specifiers - .entry(protocol_name.to_owned()) + .entry(protocol_name.into()) .or_default() .extend(specifier.iter().cloned()); self @@ -95,27 +108,12 @@ impl DeviceConfigurationManagerBuilder { } } - pub fn finish(&mut self) -> Result { - // Build and validate the protocol attributes tree. - let mut attribute_tree_map = HashMap::new(); - - // Add all the defaults first, they won't have parent attributes. - for (ident, attr) in &self.base_device_definitions { - attribute_tree_map.insert(ident.clone(), attr.clone()); - } - - let user_attribute_tree_map = DashMap::new(); - // Finally, add in user configurations, which will have an address. - for kv in &self.user_device_definitions { - user_attribute_tree_map.insert(kv.key().clone(), kv.value().clone()); - } - + pub fn finish(self) -> Result { Ok(DeviceConfigurationManager { - base_communication_specifiers: self.base_communication_specifiers.clone(), - user_communication_specifiers: self.user_communication_specifiers.clone(), - base_device_definitions: attribute_tree_map, - user_device_definitions: user_attribute_tree_map, - //protocol_map, + base_communication_specifiers: self.base_communication_specifiers, + user_communication_specifiers: self.user_communication_specifiers, + base_device_definitions: self.base_device_definitions, + user_device_definitions: self.user_device_definitions, }) } } @@ -137,13 +135,14 @@ pub struct DeviceConfigurationManager { /// Communication specifiers from the base device config, mapped from protocol name to vector of /// specifiers. Should not change/update during a session. #[getset(get = "pub")] - base_communication_specifiers: HashMap>, + base_communication_specifiers: HashMap>, /// Device definitions from the base device config. Should not change/update during a session. - base_device_definitions: HashMap, + #[getset(get = "pub")] + base_device_definitions: HashMap>, /// Communication specifiers provided by the user, mapped from protocol name to vector of /// specifiers. Loaded at session start, may change over life of session. #[getset(get = "pub")] - user_communication_specifiers: DashMap>, + user_communication_specifiers: DashMap>, /// Device definitions from the user device config. Loaded at session start, may change over life /// of session. #[getset(get = "pub")] @@ -175,7 +174,7 @@ impl DeviceConfigurationManager { //self.protocol_map.contains_key(protocol); self .user_communication_specifiers - .entry(protocol.to_owned()) + .entry(protocol.into()) .or_default() .push(specifier.clone()); Ok(()) @@ -287,8 +286,9 @@ impl DeviceConfigurationManager { "Protocol + Identifier device config found for {:?}, creating new user device from configuration", identifier ); - let mut builder = ServerDeviceDefinitionBuilder::from_base(definition, Uuid::new_v4(), true); - builder.index(self.device_index(identifier)).finish() + ServerDeviceDefinitionBuilder::from_base(definition, Uuid::new_v4(), true) + .index(self.device_index(identifier)) + .finish() } else if let Some(definition) = self .base_device_definitions .get(&BaseDeviceIdentifier::new(identifier.protocol(), &None)) @@ -297,8 +297,9 @@ impl DeviceConfigurationManager { "Protocol device config found for {:?}, creating new user device from protocol defaults", identifier ); - let mut builder = ServerDeviceDefinitionBuilder::from_base(definition, Uuid::new_v4(), true); - builder.index(self.device_index(identifier)).finish() + ServerDeviceDefinitionBuilder::from_base(definition, Uuid::new_v4(), true) + .index(self.device_index(identifier)) + .finish() } else { return None; }; diff --git a/crates/buttplug_server_device_config/src/device_definitions.rs b/crates/buttplug_server_device_config/src/device_definitions.rs index 27df1832c..c7a8848e4 100644 --- a/crates/buttplug_server_device_config/src/device_definitions.rs +++ b/crates/buttplug_server_device_config/src/device_definitions.rs @@ -5,27 +5,28 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. -use std::collections::BTreeMap; - +use compact_str::CompactString; use getset::{CopyGetters, Getters}; +use litemap::LiteMap; +use serde::{Deserialize, Serialize}; use uuid::Uuid; use super::server_device_feature::ServerDeviceFeature; -#[derive(Debug, Clone, Getters, CopyGetters)] +#[derive(Debug, Clone, Getters, CopyGetters, Serialize, Deserialize)] pub struct ServerDeviceDefinition { #[getset(get = "pub")] /// Given name of the device this instance represents. - name: String, + name: CompactString, #[getset(get_copy = "pub")] id: Uuid, #[getset(get_copy = "pub")] base_id: Option, #[getset(get = "pub")] - protocol_variant: Option, + protocol_variant: Option, #[getset(get_copy = "pub")] message_gap_ms: Option, #[getset(get = "pub")] - display_name: Option, + display_name: Option, #[getset(get_copy = "pub")] allow: bool, #[getset(get_copy = "pub")] @@ -37,7 +38,7 @@ pub struct ServerDeviceDefinition { // Older versions of the protocol expect specific ordering, so we need to make sure storage // adheres to that since we do a lot of value iteration elsewhere. #[getset(get = "pub")] - features: BTreeMap, + features: LiteMap, } #[derive(Debug)] @@ -46,11 +47,37 @@ pub struct ServerDeviceDefinitionBuilder { } impl ServerDeviceDefinitionBuilder { - pub fn new(name: &str, id: &Uuid) -> Self { + pub fn new(name: CompactString, id: Uuid) -> Self { + Self { + def: ServerDeviceDefinition { + name, + id, + base_id: None, + protocol_variant: None, + message_gap_ms: None, + display_name: None, + allow: false, + deny: false, + index: 0, + features: LiteMap::new(), + }, + } + } + + pub fn new_with_features( + name: CompactString, + id: Uuid, + features_iter: impl Iterator, + ) -> Self { + // LiteMap's .collect() doesn't take capacity into account + let mut features = LiteMap::with_capacity(features_iter.size_hint().0); + for feature in features_iter { + features.insert(feature.index(), feature); + } Self { def: ServerDeviceDefinition { - name: name.to_owned(), - id: *id, + name, + id, base_id: None, protocol_variant: None, message_gap_ms: None, @@ -58,7 +85,7 @@ impl ServerDeviceDefinitionBuilder { allow: false, deny: false, index: 0, - features: BTreeMap::new(), + features, }, } } @@ -78,7 +105,7 @@ impl ServerDeviceDefinitionBuilder { }) .collect(); } else { - value.features = BTreeMap::new(); + value.features = LiteMap::new(); } ServerDeviceDefinitionBuilder { def: value } } @@ -87,64 +114,64 @@ impl ServerDeviceDefinitionBuilder { ServerDeviceDefinitionBuilder { def: value.clone() } } - pub fn id(&mut self, id: Uuid) -> &mut Self { + pub fn id(mut self, id: Uuid) -> Self { self.def.id = id; self } - pub fn base_id(&mut self, id: Uuid) -> &mut Self { + pub fn base_id(mut self, id: Uuid) -> Self { self.def.base_id = Some(id); self } - pub fn display_name(&mut self, name: &Option) -> &mut Self { + pub fn display_name(mut self, name: Option) -> Self { self.def.display_name = name.clone(); self } - pub fn protocol_variant(&mut self, variant: &str) -> &mut Self { - self.def.protocol_variant = Some(variant.to_owned()); + pub fn protocol_variant(mut self, variant: Option) -> Self { + self.def.protocol_variant = variant; self } - pub fn message_gap_ms(&mut self, gap: Option) -> &mut Self { + pub fn message_gap_ms(mut self, gap: Option) -> Self { self.def.message_gap_ms = gap; self } - pub fn allow(&mut self, allow: bool) -> &mut Self { + pub fn allow(mut self, allow: bool) -> Self { self.def.allow = allow; self } - pub fn deny(&mut self, deny: bool) -> &mut Self { + pub fn deny(mut self, deny: bool) -> Self { self.def.deny = deny; self } - pub fn index(&mut self, index: u32) -> &mut Self { + pub fn index(mut self, index: u32) -> Self { self.def.index = index; self } - pub fn add_feature(&mut self, feature: &ServerDeviceFeature) -> &mut Self { - self.def.features.insert(feature.index(), feature.clone()); + pub fn add_feature(mut self, feature: ServerDeviceFeature) -> Self { + self.def.features.insert(feature.index(), feature); self } - pub fn replace_feature(&mut self, feature: &ServerDeviceFeature) -> &mut Self { + pub fn replace_feature(mut self, feature: ServerDeviceFeature) -> Self { if let Some((_, f)) = self .def .features .iter_mut() .find(|(_, x)| x.id() == feature.id()) { - *f = feature.clone(); + *f = feature; } self } - pub fn finish(&self) -> ServerDeviceDefinition { - self.def.clone() + pub fn finish(self) -> ServerDeviceDefinition { + self.def } } diff --git a/crates/buttplug_server_device_config/src/identifiers.rs b/crates/buttplug_server_device_config/src/identifiers.rs index 5edafb6af..6a24cdb24 100644 --- a/crates/buttplug_server_device_config/src/identifiers.rs +++ b/crates/buttplug_server_device_config/src/identifiers.rs @@ -5,6 +5,7 @@ // Licensed under the BSD 3-Clause license. See LICENSE file in the project root // for full license information. +use compact_str::CompactString; use getset::{Getters, MutGetters}; use serde::{Deserialize, Serialize}; @@ -22,32 +23,32 @@ use serde::{Deserialize, Serialize}; #[getset(get = "pub", get_mut = "pub(crate)")] pub struct UserDeviceIdentifier { /// Name of the protocol used - protocol: String, + protocol: CompactString, /// Internal identifier for the protocol used - identifier: Option, + identifier: Option, /// Address, as possibly serialized by whatever the managing library for the Device Communication Manager is. - address: String, + address: CompactString, } impl UserDeviceIdentifier { /// Creates a new instance - pub fn new(address: &str, protocol: &str, identifier: &Option) -> Self { + pub fn new(address: &str, protocol: &str, identifier: Option<&str>) -> Self { Self { - address: address.to_owned(), - protocol: protocol.to_owned(), - identifier: identifier.clone(), + address: address.into(), + protocol: protocol.into(), + identifier: identifier.map(|s| s.into()) } } } /// Set of information used for matching devices to their features and related communication protocol. #[derive(Debug, Clone, PartialEq, Eq, Hash, Getters, MutGetters, Serialize, Deserialize)] -#[getset(get = "pub(crate)", get_mut = "pub(crate)")] +#[getset(get = "pub", get_mut = "pub(crate)")] pub struct BaseDeviceIdentifier { /// Name of the protocol this device uses to communicate - protocol: String, + protocol: CompactString, /// Some([identifier]) if there's an identifier, otherwise None if default - identifier: Option, + identifier: Option, } impl BaseDeviceIdentifier { @@ -55,13 +56,16 @@ impl BaseDeviceIdentifier { Self::new(protocol, &None) } - pub fn new_with_identifier(protocol: &str, attributes_identifier: &str) -> Self { - Self::new(protocol, &Some(attributes_identifier.to_owned())) + pub fn new_with_identifier(protocol: &str, attributes_identifier: CompactString) -> Self { + Self { + protocol: protocol.into(), + identifier: Some(attributes_identifier), + } } - pub fn new(protocol: &str, attributes_identifier: &Option) -> Self { + pub fn new(protocol: &str, attributes_identifier: &Option) -> Self { Self { - protocol: protocol.to_owned(), + protocol: protocol.into(), identifier: attributes_identifier.clone(), } } diff --git a/crates/buttplug_server_device_config/src/lib.rs b/crates/buttplug_server_device_config/src/lib.rs index 953d24c87..587d743e0 100644 --- a/crates/buttplug_server_device_config/src/lib.rs +++ b/crates/buttplug_server_device_config/src/lib.rs @@ -159,30 +159,26 @@ pub use endpoint::*; use uuid::Uuid; use thiserror::Error; +use displaydoc::Display; -#[derive(Error, Debug)] +#[derive(Error, Debug, Display)] pub enum ButtplugDeviceConfigError { - /// Conversion to client type not possible with requested property type - #[error("Conversion of {0} to client type not possible with requested property type")] + /// Conversion of {0} to client type not possible with requested property type InvalidOutputTypeConversion(String), /// User set range exceeds bounds of possible configuration range - #[error("User set range exceeds bounds of possible configuration range")] InvalidUserRange, - /// Base range required - #[error("Base range required for all feature outputs")] + /// Base range required for all feature outputs BaseRangeRequired, - /// Base ID not found, cannot match user device/feature to a base device/feature - #[error("Device definition with base id {0} not found")] + /// Device definition with base id {0} not found BaseIdNotFound(Uuid), - #[error("Feature vectors between base and user device definitions do not match")] + /// Feature vectors between base and user device definitions do not match UserFeatureMismatch, - #[error("Output value {0} not in range {1}")] + /// Output value {0} not in range {1} InvalidOutputValue(i32, String), - #[error("Output type {0} not available on device")] + /// Output type {0} not available on device InvalidOutput(OutputType), - #[error("Float value {0} is not 0 < x < 1")] + /// Float value {0} is not 0 < x < 1 InvalidFloatConversion(f64), /// Feature or device is missing required base_id for user config conversion - #[error("Feature or device is missing required base_id for user config conversion")] MissingBaseId, } diff --git a/crates/buttplug_server_device_config/src/server_device_feature.rs b/crates/buttplug_server_device_config/src/server_device_feature.rs index 74163ad8a..62be47205 100644 --- a/crates/buttplug_server_device_config/src/server_device_feature.rs +++ b/crates/buttplug_server_device_config/src/server_device_feature.rs @@ -7,69 +7,34 @@ use crate::ButtplugDeviceConfigError; -use buttplug_core::message::{ - DeviceFeature, - DeviceFeatureInput, - DeviceFeatureInputBuilder, - DeviceFeatureInputProperties, - DeviceFeatureOutput, - DeviceFeatureOutputBuilder, - DeviceFeatureOutputHwPositionWithDurationProperties, - DeviceFeatureOutputValueProperties, - InputCommandType, - InputType, - OutputType, +use buttplug_core::{ + message::{ + DeviceFeature, + DeviceFeatureInput, + DeviceFeatureInputBuilder, + DeviceFeatureInputProperties, + DeviceFeatureOutput, + DeviceFeatureOutputBuilder, + DeviceFeatureOutputHwPositionWithDurationProperties, + DeviceFeatureOutputValueProperties, + InputCommandType, + InputCommandTypeFlags, + InputType, + OutputType, + }, + util::range::RangeInclusive, }; +use compact_str::CompactString; use getset::{CopyGetters, Getters, Setters}; use serde::{ + Deserialize, + Serialize, + Serializer, de::{self, Deserializer, SeqAccess, Visitor}, ser::SerializeSeq, - Deserialize, Serialize, Serializer, }; -use std::{collections::HashSet, fmt, ops::RangeInclusive}; use uuid::Uuid; -/// Serde helper module for serializing/deserializing Vec> as [[start, end], ...] -mod range_vec_serde { - use super::*; - - pub fn serialize(ranges: &Vec>, serializer: S) -> Result - where - S: Serializer, - { - let arrays: Vec<[i32; 2]> = ranges.iter().map(|r| [*r.start(), *r.end()]).collect(); - arrays.serialize(serializer) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> - where - D: Deserializer<'de>, - { - struct RangeVecVisitor; - - impl<'de> Visitor<'de> for RangeVecVisitor { - type Value = Vec>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an array of two-element arrays [[start, end], ...]") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut ranges = Vec::new(); - while let Some([start, end]) = seq.next_element::<[i32; 2]>()? { - ranges.push(start..=end); - } - Ok(ranges) - } - } - - deserializer.deserialize_seq(RangeVecVisitor) - } -} - /// Holds a combination of ranges. Base range is defined in the base device config, user range is /// defined by the user later to be a sub-range of the base range. User range only stores in u32, /// ranges with negatives (i.e. rotate with direction) are considered to be symettric around 0, we @@ -92,7 +57,7 @@ impl RangeWithLimit { pub fn new(base: &RangeInclusive) -> Self { Self { base: base.clone(), - internal_base: RangeInclusive::new(0, *base.end() as u32), + internal_base: RangeInclusive::new(0, base.end() as u32), user: None, } } @@ -100,13 +65,13 @@ impl RangeWithLimit { pub fn new_with_user(base: &RangeInclusive, user: &Option>) -> Self { Self { base: base.clone(), - internal_base: RangeInclusive::new(0, *base.end() as u32), + internal_base: RangeInclusive::new(0, base.end() as u32), user: user.clone(), } } pub fn step_limit(&self) -> RangeInclusive { - if *self.base.start() < 0 { + if self.base.start() < 0 { RangeInclusive::new(-(self.step_count() as i32), self.step_count() as i32) } else { RangeInclusive::new(0, self.step_count() as i32) @@ -115,9 +80,9 @@ impl RangeWithLimit { pub fn step_count(&self) -> u32 { if let Some(user) = &self.user { - *user.end() - *user.start() + user.end() - user.start() } else { - *self.base.end() as u32 + self.base.end() as u32 } } @@ -125,14 +90,14 @@ impl RangeWithLimit { base: &RangeInclusive, user: &Option>, ) -> Result { - let truncated_base = RangeInclusive::new(0, *base.end() as u32); + let truncated_base = RangeInclusive::new(0, base.end() as u32); if let Some(user) = user { if user.is_empty() { Err(ButtplugDeviceConfigError::InvalidUserRange) - } else if *user.start() < *truncated_base.start() - || *user.end() > *truncated_base.end() - || *user.start() > *truncated_base.end() - || *user.end() < *truncated_base.start() + } else if user.start() < truncated_base.start() + || user.end() > truncated_base.end() + || user.start() > truncated_base.end() + || user.end() < truncated_base.start() { Err(ButtplugDeviceConfigError::InvalidUserRange) } else { @@ -160,8 +125,8 @@ impl Serialize for RangeWithLimit { S: Serializer, { let mut seq = serializer.serialize_seq(Some(2))?; - seq.serialize_element(self.base.start())?; - seq.serialize_element(self.base.end())?; + seq.serialize_element(&self.base.start())?; + seq.serialize_element(&self.base.end())?; seq.end() } } @@ -190,7 +155,7 @@ impl<'de> Deserialize<'de> for RangeWithLimit { let end: i32 = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(1, &self))?; - Ok(RangeWithLimit::new(&(start..=end))) + Ok(RangeWithLimit::new(&RangeInclusive::new(start, end))) } } @@ -234,7 +199,7 @@ impl ServerDeviceFeatureOutputValueProperties { }; let current_value = value.unsigned_abs(); let mult = if value < 0 { -1 } else { 1 }; - if value != 0 && range.contains(&(range.start() + current_value)) { + if value != 0 && range.contains(range.start() + current_value) { Ok((range.start() + current_value) as i32 * mult) } else if value == 0 { Ok(0) @@ -291,7 +256,7 @@ impl ServerDeviceFeatureOutputPositionProperties { } else { self.value.internal_base() }; - if range.contains(&(range.start() + input)) { + if range.contains(range.start() + input) { if self.reverse_position { Ok(range.end() - input) } else { @@ -352,7 +317,7 @@ impl ServerDeviceFeatureOutputHwPositionWithDurationProperties { } else { self.value.internal_base() }; - if input > 0 && range.contains(&(range.start() + input)) { + if input > 0 && range.contains(range.start() + input) { if self.reverse_position { Ok(range.end() - input) } else { @@ -429,7 +394,10 @@ impl ServerDeviceFeatureOutput { (self.temperature.is_some(), OutputType::Temperature), (self.led.is_some(), OutputType::Led), (self.position.is_some(), OutputType::Position), - (self.hw_position_with_duration.is_some(), OutputType::HwPositionWithDuration), + ( + self.hw_position_with_duration.is_some(), + OutputType::HwPositionWithDuration, + ), (self.spray.is_some(), OutputType::Spray), ] .into_iter() @@ -556,26 +524,22 @@ impl From for DeviceFeatureOutput { #[derive(Clone, Debug, Getters, Serialize, Deserialize)] #[getset(get = "pub")] pub struct ServerDeviceFeatureInputProperties { - #[serde(with = "range_vec_serde")] value: Vec>, - command: HashSet, + command: InputCommandTypeFlags, } impl ServerDeviceFeatureInputProperties { - pub fn new( - value: &Vec>, - sensor_commands: &HashSet, - ) -> Self { + pub fn new(value: &Vec>, sensor_commands: InputCommandTypeFlags) -> Self { Self { value: value.clone(), - command: sensor_commands.clone(), + command: sensor_commands, } } } impl From<&ServerDeviceFeatureInputProperties> for DeviceFeatureInputProperties { fn from(val: &ServerDeviceFeatureInputProperties) -> Self { - DeviceFeatureInputProperties::new(&val.value, &val.command) + DeviceFeatureInputProperties::new(&val.value, val.command.clone()) } } @@ -623,7 +587,7 @@ impl ServerDeviceFeatureInput { .any(|input| { input .as_ref() - .map_or(false, |i| i.command.contains(&InputCommandType::Subscribe)) + .map_or(false, |i| i.command.contains(InputCommandType::Subscribe)) }) } } @@ -645,13 +609,11 @@ impl From for DeviceFeatureInput { #[serde(default)] pub struct ServerDeviceFeature { #[getset(get_copy = "pub")] - #[serde(skip)] index: u32, #[getset(get = "pub")] #[serde(default)] - description: String, + description: CompactString, #[getset(get_copy = "pub")] - #[serde(skip)] id: Uuid, #[getset(get_copy = "pub")] #[serde(skip_serializing_if = "Option::is_none")] @@ -673,13 +635,14 @@ impl PartialEq for ServerDeviceFeature { } } -impl Eq for ServerDeviceFeature {} +impl Eq for ServerDeviceFeature { +} impl Default for ServerDeviceFeature { fn default() -> Self { Self { index: 0, - description: String::new(), + description: "".into(), id: Uuid::new_v4(), base_id: None, alt_protocol_index: None, @@ -692,21 +655,21 @@ impl Default for ServerDeviceFeature { impl ServerDeviceFeature { pub fn new( index: u32, - description: &str, + description: CompactString, id: Uuid, base_id: Option, alt_protocol_index: Option, - output: &Option, - input: &Option, + output: Option, + input: Option, ) -> Self { Self { index, - description: description.to_owned(), + description, id, base_id, alt_protocol_index, - output: output.clone(), - input: input.clone(), + output, + input, } } diff --git a/crates/buttplug_server_device_config/src/specifier.rs b/crates/buttplug_server_device_config/src/specifier.rs index 2c3f9746c..e85e1c539 100644 --- a/crates/buttplug_server_device_config/src/specifier.rs +++ b/crates/buttplug_server_device_config/src/specifier.rs @@ -6,6 +6,7 @@ // for full license information. use super::Endpoint; +use compact_str::CompactString; use getset::{Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -85,7 +86,7 @@ impl PartialEq for BluetoothLEManufacturerData { #[getset(get = "pub", set = "pub", get_mut = "pub(crate)")] pub struct BluetoothLESpecifier { /// Set of expected advertised names for this device. - names: HashSet, + names: HashSet, /// Array of possible manufacturer data values. #[serde(default)] manufacturer_data: Vec, @@ -107,8 +108,8 @@ impl PartialEq for BluetoothLESpecifier { // Otherwise, try wildcarded names. for name in &self.names { for other_name in &other.names { - let compare_name: &String; - let mut wildcard: String; + let compare_name: &CompactString; + let mut wildcard: CompactString; if name.ends_with('*') { wildcard = name.clone(); compare_name = other_name; @@ -120,7 +121,7 @@ impl PartialEq for BluetoothLESpecifier { } // Remove asterisk from the end of the wildcard wildcard.pop(); - if compare_name.starts_with(&wildcard) { + if compare_name.starts_with(wildcard.as_str()) { return true; } } @@ -149,7 +150,7 @@ impl PartialEq for BluetoothLESpecifier { impl BluetoothLESpecifier { pub fn new( - names: HashSet, + names: HashSet, manufacturer_data: Vec, advertised_services: HashSet, services: HashMap>, @@ -169,7 +170,7 @@ impl BluetoothLESpecifier { advertised_services: &[Uuid], ) -> BluetoothLESpecifier { let mut name_set = HashSet::new(); - name_set.insert(name.to_string()); + name_set.insert(name.into()); let mut data_vec = vec![]; for (company, data) in manufacturer_data.iter() { data_vec.push(BluetoothLEManufacturerData::new( diff --git a/crates/buttplug_server_device_config/tests/test_device_config.rs b/crates/buttplug_server_device_config/tests/test_device_config.rs index 5abf83bb7..ec75f0f4a 100644 --- a/crates/buttplug_server_device_config/tests/test_device_config.rs +++ b/crates/buttplug_server_device_config/tests/test_device_config.rs @@ -65,7 +65,7 @@ fn test_tcode_device_creation() { .finish() .unwrap(); let device = dcm - .device_definition(&UserDeviceIdentifier::new("COM1", "tcode-v03", &None)) + .device_definition(&UserDeviceIdentifier::new("COM1", "tcode-v03", None)) .unwrap(); assert_eq!(device.name(), "TCode v0.3 (Single Linear Axis)"); } diff --git a/crates/buttplug_server_hwmgr_btleplug/Cargo.toml b/crates/buttplug_server_hwmgr_btleplug/Cargo.toml index 137456d62..bbae3871a 100644 --- a/crates/buttplug_server_hwmgr_btleplug/Cargo.toml +++ b/crates/buttplug_server_hwmgr_btleplug/Cargo.toml @@ -20,7 +20,7 @@ doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" diff --git a/crates/buttplug_server_hwmgr_btleplug/src/btleplug_comm_manager.rs b/crates/buttplug_server_hwmgr_btleplug/src/btleplug_comm_manager.rs index 957b9a4c5..1a32f1065 100644 --- a/crates/buttplug_server_hwmgr_btleplug/src/btleplug_comm_manager.rs +++ b/crates/buttplug_server_hwmgr_btleplug/src/btleplug_comm_manager.rs @@ -18,6 +18,7 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, }; use tokio::sync::mpsc::{Sender, channel}; +use tracing::info_span; #[derive(Default, Clone)] pub struct BtlePlugCommunicationManagerBuilder { @@ -57,15 +58,18 @@ impl BtlePlugCommunicationManager { let (sender, receiver) = channel(256); let adapter_connected = Arc::new(AtomicBool::new(false)); let adapter_connected_clone = adapter_connected.clone(); - async_manager::spawn(async move { - let mut task = BtleplugAdapterTask::new( - event_sender, - receiver, - adapter_connected_clone, - require_keepalive, - ); - task.run().await; - }); + async_manager::spawn( + async move { + let mut task = BtleplugAdapterTask::new( + event_sender, + receiver, + adapter_connected_clone, + require_keepalive, + ); + task.run().await; + }, + info_span!("BtlePlugCommunicationManager::adapter_task").or_current(), + ); Self { adapter_event_sender: sender, scanning_status: Arc::new(AtomicBool::new(false)), diff --git a/crates/buttplug_server_hwmgr_btleplug/src/btleplug_hardware.rs b/crates/buttplug_server_hwmgr_btleplug/src/btleplug_hardware.rs index 061ff44c0..01d206f2b 100644 --- a/crates/buttplug_server_hwmgr_btleplug/src/btleplug_hardware.rs +++ b/crates/buttplug_server_hwmgr_btleplug/src/btleplug_hardware.rs @@ -44,6 +44,7 @@ use std::{ time::Duration, }; use tokio::{select, sync::broadcast}; +use tracing::info_span; use uuid::Uuid; pub(super) struct BtleplugHardwareConnector { @@ -243,70 +244,73 @@ impl BtlePlugHardware { let event_stream_clone = event_stream.clone(); let address = device.id(); let name_clone = name.to_owned(); - async_manager::spawn(async move { - let mut error_notification = false; - loop { - select! { - notification = notification_stream.next() => { - if let Some(notification) = notification { - let endpoint = if let Some(endpoint) = uuid_map.get(¬ification.uuid) { - *endpoint - } else { - // Only print the error message once. - if !error_notification { + async_manager::spawn( + async move { + let mut error_notification = false; + loop { + select! { + notification = notification_stream.next() => { + if let Some(notification) = notification { + let endpoint = if let Some(endpoint) = uuid_map.get(¬ification.uuid) { + *endpoint + } else { + // Only print the error message once. + if !error_notification { + error!( + "Endpoint for UUID {} not found in map, assuming device has disconnected.", + notification.uuid + ); + error_notification = true; + } + continue; + }; + if event_stream_clone.receiver_count() == 0 { + continue; + } + if let Err(err) = event_stream_clone.send(HardwareEvent::Notification( + format!("{address:?}"), + endpoint, + notification.value, + )) { error!( - "Endpoint for UUID {} not found in map, assuming device has disconnected.", - notification.uuid + "Cannot send notification, device object disappeared: {:?}", + err ); - error_notification = true; + break; } - continue; - }; - if event_stream_clone.receiver_count() == 0 { - continue; - } - if let Err(err) = event_stream_clone.send(HardwareEvent::Notification( - format!("{address:?}"), - endpoint, - notification.value, - )) { - error!( - "Cannot send notification, device object disappeared: {:?}", - err - ); - break; } } - } - adapter_event = adapter_event_stream.next() => { - if let Some(CentralEvent::DeviceDisconnected(addr)) = adapter_event - && address == addr { - info!( - "Device {:?} disconnected", - name_clone - ); - if event_stream_clone.receiver_count() != 0 - && let Err(err) = event_stream_clone - .send(HardwareEvent::Disconnected( - format!("{address:?}") - )) { - error!( - "Cannot send notification, device object disappeared: {:?}", - err - ); - } - // At this point, we have nothing left to do because we can't reconnect a device - // that's been connected. Exit. - break; - } + adapter_event = adapter_event_stream.next() => { + if let Some(CentralEvent::DeviceDisconnected(addr)) = adapter_event + && address == addr { + info!( + "Device {:?} disconnected", + name_clone + ); + if event_stream_clone.receiver_count() != 0 + && let Err(err) = event_stream_clone + .send(HardwareEvent::Disconnected( + format!("{address:?}") + )) { + error!( + "Cannot send notification, device object disappeared: {:?}", + err + ); + } + // At this point, we have nothing left to do because we can't reconnect a device + // that's been connected. Exit. + break; + } + } } } - } - info!( - "Exiting btleplug notification/event loop for device {:?}", - address - ) - }); + info!( + "Exiting btleplug notification/event loop for device {:?}", + address + ) + }, + info_span!("BtlePlugHardware::event_loop").or_current(), + ); Self { device, endpoints, @@ -517,10 +521,13 @@ impl HardwareInternal for BtlePlugHardware { impl Drop for BtlePlugHardware { fn drop(&mut self) { let disconnect_fut = self.disconnect(); - async_manager::spawn(async move { - if let Err(e) = disconnect_fut.await { - error!("Error disconnecting btleplug device: {:?}", e); - } - }); + async_manager::spawn( + async move { + if let Err(e) = disconnect_fut.await { + error!("Error disconnecting btleplug device: {:?}", e); + } + }, + info_span!("BtlePlugHardware::drop").or_current(), + ); } } diff --git a/crates/buttplug_server_hwmgr_hid/Cargo.toml b/crates/buttplug_server_hwmgr_hid/Cargo.toml index bd0572ed1..d8b84a943 100644 --- a/crates/buttplug_server_hwmgr_hid/Cargo.toml +++ b/crates/buttplug_server_hwmgr_hid/Cargo.toml @@ -20,7 +20,7 @@ doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" diff --git a/crates/buttplug_server_hwmgr_lovense_connect/Cargo.toml b/crates/buttplug_server_hwmgr_lovense_connect/Cargo.toml index 55aa206eb..6be18d56f 100644 --- a/crates/buttplug_server_hwmgr_lovense_connect/Cargo.toml +++ b/crates/buttplug_server_hwmgr_lovense_connect/Cargo.toml @@ -27,7 +27,7 @@ targets = [] features = ["default", "unstable"] [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" diff --git a/crates/buttplug_server_hwmgr_lovense_connect/src/lovense_connect_service_hardware.rs b/crates/buttplug_server_hwmgr_lovense_connect/src/lovense_connect_service_hardware.rs index 45cc145f4..84d45f0e9 100644 --- a/crates/buttplug_server_hwmgr_lovense_connect/src/lovense_connect_service_hardware.rs +++ b/crates/buttplug_server_hwmgr_lovense_connect/src/lovense_connect_service_hardware.rs @@ -36,6 +36,7 @@ use std::{ time::Duration, }; use tokio::sync::broadcast; +use tracing::info_span; pub struct LovenseServiceHardwareConnector { http_host: String, @@ -93,33 +94,36 @@ impl LovenseServiceHardware { let host = http_host.to_owned(); let battery_level = Arc::new(AtomicU8::new(100)); let battery_level_clone = battery_level.clone(); - async_manager::spawn(async move { - loop { - // SutekhVRC/VibeCheck patch for delay because Lovense Connect HTTP servers crash (Perma DOS) - tokio::time::sleep(Duration::from_secs(1)).await; - match get_local_info(&host).await { - Some(info) => { - for (_, toy) in info.data.iter() { - if toy.id != toy_id { - continue; - } - if !toy.connected { - let _ = sender_clone.send(HardwareEvent::Disconnected(toy_id.clone())); - info!("Exiting lovense service device connection check loop."); + async_manager::spawn( + async move { + loop { + // SutekhVRC/VibeCheck patch for delay because Lovense Connect HTTP servers crash (Perma DOS) + async_manager::sleep(Duration::from_secs(1)).await; + match get_local_info(&host).await { + Some(info) => { + for (_, toy) in info.data.iter() { + if toy.id != toy_id { + continue; + } + if !toy.connected { + let _ = sender_clone.send(HardwareEvent::Disconnected(toy_id.clone())); + info!("Exiting lovense service device connection check loop."); + break; + } + battery_level_clone.store(toy.battery.clamp(0, 100) as u8, Ordering::Relaxed); break; } - battery_level_clone.store(toy.battery.clamp(0, 100) as u8, Ordering::Relaxed); + } + None => { + let _ = sender_clone.send(HardwareEvent::Disconnected(toy_id.clone())); + info!("Exiting lovense service device connection check loop."); break; } } - None => { - let _ = sender_clone.send(HardwareEvent::Disconnected(toy_id.clone())); - info!("Exiting lovense service device connection check loop."); - break; - } } - } - }); + }, + info_span!("LovenseServiceHardware::connection_check").or_current(), + ); Self { event_sender: device_event_sender, http_host: http_host.to_owned(), @@ -167,12 +171,15 @@ impl HardwareInternal for LovenseServiceHardware { async move { match reqwest::get(command_url).await { Ok(res) => { - async_manager::spawn(async move { - trace!( - "Got http response: {}", - res.text().await.unwrap_or("no response".to_string()) - ); - }); + async_manager::spawn( + async move { + trace!( + "Got http response: {}", + res.text().await.unwrap_or("no response".to_string()) + ); + }, + info_span!("LovenseServiceHardware::write_value_response").or_current(), + ); Ok(()) } Err(err) => { diff --git a/crates/buttplug_server_hwmgr_lovense_dongle/Cargo.toml b/crates/buttplug_server_hwmgr_lovense_dongle/Cargo.toml index 1466c6314..8294b43cc 100644 --- a/crates/buttplug_server_hwmgr_lovense_dongle/Cargo.toml +++ b/crates/buttplug_server_hwmgr_lovense_dongle/Cargo.toml @@ -20,7 +20,7 @@ doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" @@ -36,7 +36,6 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" serde_repr = "0.1.20" tokio-util = "0.7.18" -tracing-futures = "0.2.5" [target.'cfg(target_os = "windows")'.dependencies] hidapi = { version = "2.6.4", default-features = false, features = ["windows-native"] } diff --git a/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_dongle_hardware.rs b/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_dongle_hardware.rs index ea0b82f35..26dc68fd3 100644 --- a/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_dongle_hardware.rs +++ b/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_dongle_hardware.rs @@ -33,6 +33,7 @@ use buttplug_server_device_config::{ ProtocolCommunicationSpecifier, }; use futures::future::{self, BoxFuture, FutureExt}; +use tracing::info_span; use std::{ collections::HashMap, fmt::{self, Debug}, @@ -128,39 +129,42 @@ impl LovenseDongleHardware { let address_clone = address.to_owned(); let (device_event_sender, _) = broadcast::channel(256); let device_event_sender_clone = device_event_sender.clone(); - async_manager::spawn(async move { - while let Some(msg) = device_incoming.recv().await { - if msg.func != LovenseDongleMessageFunc::ToyData { - continue; + async_manager::spawn( + async move { + while let Some(msg) = device_incoming.recv().await { + if msg.func != LovenseDongleMessageFunc::ToyData { + continue; + } + let data_str = msg + .data + .expect("USB format shouldn't change") + .data + .expect("USB format shouldn't change"); + if device_event_sender_clone + .send(HardwareEvent::Notification( + address_clone.clone(), + Endpoint::Rx, + data_str.into_bytes(), + )) + .is_err() + { + // This sometimes happens with the serial dongle, not sure why. I + // think it may have to do some sort of connection timing. It seems + // like we can continue through it and be fine? Who knows. God I + // hate the lovense dongle. + error!("Can't send to device event sender, continuing Lovense dongle loop."); + } } - let data_str = msg - .data - .expect("USB format shouldn't change") - .data - .expect("USB format shouldn't change"); + info!("Lovense dongle device disconnected",); if device_event_sender_clone - .send(HardwareEvent::Notification( - address_clone.clone(), - Endpoint::Rx, - data_str.into_bytes(), - )) + .send(HardwareEvent::Disconnected(address_clone.clone())) .is_err() { - // This sometimes happens with the serial dongle, not sure why. I - // think it may have to do some sort of connection timing. It seems - // like we can continue through it and be fine? Who knows. God I - // hate the lovense dongle. - error!("Can't send to device event sender, continuing Lovense dongle loop."); + error!("Device Manager no longer alive, cannot send removed event."); } - } - info!("Lovense dongle device disconnected",); - if device_event_sender_clone - .send(HardwareEvent::Disconnected(address_clone.clone())) - .is_err() - { - error!("Device Manager no longer alive, cannot send removed event."); - } - }); + }, + info_span!("LovenseDongleHardware::device_incoming_loop").or_current(), + ); Self { address: address.to_owned(), device_outgoing, diff --git a/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_hid_dongle_comm_manager.rs b/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_hid_dongle_comm_manager.rs index 23ace40e8..96ae9170c 100644 --- a/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_hid_dongle_comm_manager.rs +++ b/crates/buttplug_server_hwmgr_lovense_dongle/src/lovense_hid_dongle_comm_manager.rs @@ -38,7 +38,7 @@ use tokio::{ }, }; use tokio_util::sync::CancellationToken; -use tracing_futures::Instrument; +use tracing::info_span; fn hid_write_thread( dongle: HidDevice, @@ -193,8 +193,8 @@ impl LovenseHIDDongleCommunicationManager { async_manager::spawn( async move { let _ = dongle_fut.await; - } - .instrument(tracing::info_span!("Lovense HID Dongle Finder Task")), + }, + info_span!("Lovense HID Dongle Finder Task").or_current(), ); let mut machine = create_lovense_dongle_machine(event_sender, machine_receiver, mgr.is_scanning.clone()); @@ -203,8 +203,8 @@ impl LovenseHIDDongleCommunicationManager { while let Some(next) = machine.transition().await { machine = next; } - } - .instrument(tracing::info_span!("Lovense HID Dongle State Machine")), + }, + info_span!("Lovense HID Dongle State Machine").or_current(), ); mgr } diff --git a/crates/buttplug_server_hwmgr_serial/Cargo.toml b/crates/buttplug_server_hwmgr_serial/Cargo.toml index 28347c09a..871107610 100644 --- a/crates/buttplug_server_hwmgr_serial/Cargo.toml +++ b/crates/buttplug_server_hwmgr_serial/Cargo.toml @@ -20,7 +20,7 @@ doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" diff --git a/crates/buttplug_server_hwmgr_serial/src/serialport_hardware.rs b/crates/buttplug_server_hwmgr_serial/src/serialport_hardware.rs index 5181f16f3..435ce21e0 100644 --- a/crates/buttplug_server_hwmgr_serial/src/serialport_hardware.rs +++ b/crates/buttplug_server_hwmgr_serial/src/serialport_hardware.rs @@ -24,6 +24,7 @@ use buttplug_server_device_config::{Endpoint, ProtocolCommunicationSpecifier, Se use futures::future; use futures::{FutureExt, future::BoxFuture}; use serialport::{SerialPort, SerialPortInfo}; +use tracing::info_span; use std::{ fmt::{self, Debug}, io::ErrorKind, @@ -348,30 +349,33 @@ impl HardwareInternal for SerialPortHardware { let event_sender = self.device_event_sender.clone(); let address = self.address.clone(); async move { - async_manager::spawn(async move { - // TODO There's only one subscribable endpoint on a serial port, so we - // should check to make sure we don't have multiple subscriptions so we - // don't deadlock. - let mut data_receiver_mut = data_receiver.lock().await; - loop { - match data_receiver_mut.recv().await { - Some(data) => { - info!("Got serial data! {:?}", data); - event_sender - .send(HardwareEvent::Notification( - address.clone(), - Endpoint::Tx, - data, - )) - .expect("As long as we're subscribed we should have a listener"); - } - None => { - info!("Data channel closed, ending serial listener task"); - break; + async_manager::spawn( + async move { + // TODO There's only one subscribable endpoint on a serial port, so we + // should check to make sure we don't have multiple subscriptions so we + // don't deadlock. + let mut data_receiver_mut = data_receiver.lock().await; + loop { + match data_receiver_mut.recv().await { + Some(data) => { + info!("Got serial data! {:?}", data); + event_sender + .send(HardwareEvent::Notification( + address.clone(), + Endpoint::Tx, + data, + )) + .expect("As long as we're subscribed we should have a listener"); + } + None => { + info!("Data channel closed, ending serial listener task"); + break; + } } } - } - }); + }, + info_span!("SerialPortHardware::subscribe").or_current(), + ); Ok(()) } .boxed() diff --git a/crates/buttplug_server_hwmgr_websocket/Cargo.toml b/crates/buttplug_server_hwmgr_websocket/Cargo.toml index 98736fec9..fc911edf1 100644 --- a/crates/buttplug_server_hwmgr_websocket/Cargo.toml +++ b/crates/buttplug_server_hwmgr_websocket/Cargo.toml @@ -27,7 +27,7 @@ targets = [] features = ["default", "unstable"] [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" diff --git a/crates/buttplug_server_hwmgr_websocket/src/websocket_server_comm_manager.rs b/crates/buttplug_server_hwmgr_websocket/src/websocket_server_comm_manager.rs index aa693bb0c..1ab41d264 100644 --- a/crates/buttplug_server_hwmgr_websocket/src/websocket_server_comm_manager.rs +++ b/crates/buttplug_server_hwmgr_websocket/src/websocket_server_comm_manager.rs @@ -17,6 +17,7 @@ use getset::{CopyGetters, Getters}; use serde::{Deserialize, Serialize}; use tokio::{net::TcpListener, select, sync::mpsc::Sender}; use tokio_util::sync::CancellationToken; +use tracing::info_span; // Packet format received from external devices. #[derive(Serialize, Deserialize, Debug, Clone, Getters, CopyGetters)] @@ -82,90 +83,93 @@ impl WebsocketServerDeviceCommunicationManager { trace!("Websocket server port created."); let server_cancellation_token = CancellationToken::new(); let child_token = server_cancellation_token.child_token(); - async_manager::spawn(async move { - let base_addr = if listen_on_all_interfaces { - "0.0.0.0" - } else { - "127.0.0.1" - }; - - let addr = format!("{base_addr}:{port}"); - debug!("Trying to listen on {}", addr); - - // Create the event loop and TCP listener we'll accept connections on. - debug!("Socket bound."); - let listener = match TcpListener::bind(&addr).await { - Ok(listener) => listener, - Err(err) => { - error!("Cannot bind websocket server to {}: {:?}.", addr, err); - return; - } - }; - debug!("Listening on: {}", addr); - loop { - select! { - listener_result = listener.accept() => { - let stream = if let Ok((stream, _)) = listener_result { - stream - } else { - error!("Cannot bind websocket server comm manager to address {}.", addr); - return; - }; - info!("Got connection"); - let ws_fut = tokio_tungstenite::accept_async(stream); - let mut ws_stream = match ws_fut.await { - Ok(ws_stream) => ws_stream, - Err(err) => { - error!("Cannot accept socket: {}", err); - continue; - } - }; - // Websockets are different from the rest of the communication managers, in that we have no - // information about the device type when we create the connection, and therefore have to - // wait for the first packet. We'll have to pass our device event sender off to the newly - // created event loop, so that it can fire once the info packet is received. - let sender_clone = sender.clone(); - tokio::spawn(async move { - // TODO Implement a receive timeout here so we don't wait forever - if let Some(Ok(tokio_tungstenite::tungstenite::Message::Text(info_message))) = - ws_stream.next().await - { - let info_packet: WebsocketServerDeviceCommManagerInitInfo = - if let Ok(packet) = serde_json::from_str(&info_message) { - packet - } else { - error!("Did not receive a valid JSON info packet as the first packet, disconnecting."); - if let Err(err) = ws_stream.close(None).await { - error!("Error closing connection: {}", err); - } - return; - }; - if sender_clone - .send(HardwareCommunicationManagerEvent::DeviceFound { - name: format!("Websocket Device {}", info_packet.identifier), - address: info_packet.address.clone(), - creator: Box::new(WebsocketServerHardwareConnector::new( - info_packet, - ws_stream, - )), - }) - .await - .is_err() + async_manager::spawn( + async move { + let base_addr = if listen_on_all_interfaces { + "0.0.0.0" + } else { + "127.0.0.1" + }; + + let addr = format!("{base_addr}:{port}"); + debug!("Trying to listen on {}", addr); + + // Create the event loop and TCP listener we'll accept connections on. + debug!("Socket bound."); + let listener = match TcpListener::bind(&addr).await { + Ok(listener) => listener, + Err(err) => { + error!("Cannot bind websocket server to {}: {:?}.", addr, err); + return; + } + }; + debug!("Listening on: {}", addr); + loop { + select! { + listener_result = listener.accept() => { + let stream = if let Ok((stream, _)) = listener_result { + stream + } else { + error!("Cannot bind websocket server comm manager to address {}.", addr); + return; + }; + info!("Got connection"); + let ws_fut = tokio_tungstenite::accept_async(stream); + let mut ws_stream = match ws_fut.await { + Ok(ws_stream) => ws_stream, + Err(err) => { + error!("Cannot accept socket: {}", err); + continue; + } + }; + // Websockets are different from the rest of the communication managers, in that we have no + // information about the device type when we create the connection, and therefore have to + // wait for the first packet. We'll have to pass our device event sender off to the newly + // created event loop, so that it can fire once the info packet is received. + let sender_clone = sender.clone(); + async_manager::spawn(async move { + // TODO Implement a receive timeout here so we don't wait forever + if let Some(Ok(tokio_tungstenite::tungstenite::Message::Text(info_message))) = + ws_stream.next().await { - error!("Device manager disappeared, exiting."); + let info_packet: WebsocketServerDeviceCommManagerInitInfo = + if let Ok(packet) = serde_json::from_str(&info_message) { + packet + } else { + error!("Did not receive a valid JSON info packet as the first packet, disconnecting."); + if let Err(err) = ws_stream.close(None).await { + error!("Error closing connection: {}", err); + } + return; + }; + if sender_clone + .send(HardwareCommunicationManagerEvent::DeviceFound { + name: format!("Websocket Device {}", info_packet.identifier), + address: info_packet.address.clone(), + creator: Box::new(WebsocketServerHardwareConnector::new( + info_packet, + ws_stream, + )), + }) + .await + .is_err() + { + error!("Device manager disappeared, exiting."); + } + } else { + error!("Did not receive info message as first packet, dropping connection."); } - } else { - error!("Did not receive info message as first packet, dropping connection."); - } - }); - }, - _ = child_token.cancelled() => { - info!("Task token cancelled, assuming websocket server comm manager shutdown."); - break; + }, info_span!("WebsocketServerDeviceCommunicationManager::connection_loop").or_current()); + }, + _ = child_token.cancelled() => { + info!("Task token cancelled, assuming websocket server comm manager shutdown."); + break; + } } } - } - }); + }, + info_span!("WebsocketServerDeviceCommunicationManager::server_loop").or_current(), + ); Self { server_cancellation_token, } diff --git a/crates/buttplug_server_hwmgr_websocket/src/websocket_server_hardware.rs b/crates/buttplug_server_hwmgr_websocket/src/websocket_server_hardware.rs index 8ae1778d4..42ae8b2b9 100644 --- a/crates/buttplug_server_hwmgr_websocket/src/websocket_server_hardware.rs +++ b/crates/buttplug_server_hwmgr_websocket/src/websocket_server_hardware.rs @@ -47,6 +47,7 @@ use tokio::{ time::sleep, }; use tokio_util::sync::CancellationToken; +use tracing::info_span; async fn run_connection_loop( address: &str, @@ -173,7 +174,7 @@ impl WebsocketServerHardwareConnector { let (device_event_sender, _) = broadcast::channel(256); let device_event_sender_clone = device_event_sender.clone(); let address = info.address().clone(); - tokio::spawn(async move { + async_manager::spawn(async move { run_connection_loop( &address, device_event_sender_clone, @@ -182,7 +183,7 @@ impl WebsocketServerHardwareConnector { incoming_broadcaster_clone, ) .await; - }); + }, info_span!("WebsocketServerHardwareConnector::connection_loop").or_current()); Self { info, outgoing_sender, @@ -305,31 +306,34 @@ impl HardwareInternal for WebsocketServerHardware { subscribed.store(true, Ordering::Relaxed); let token = CancellationToken::new(); *(subscribed_token.lock().await) = Some(token.child_token()); - async_manager::spawn(async move { - loop { - select! { - result = data_receiver.recv().fuse() => { - match result { - Ok(data) => { - debug!("Got websocket data! {:?}", data); - // We don't really care if there's no one to send the error to here. - let _ = event_sender - .send(HardwareEvent::Notification( - address.clone(), - Endpoint::Tx, - data, - )); - }, - Err(_) => break, + async_manager::spawn( + async move { + loop { + select! { + result = data_receiver.recv().fuse() => { + match result { + Ok(data) => { + debug!("Got websocket data! {:?}", data); + // We don't really care if there's no one to send the error to here. + let _ = event_sender + .send(HardwareEvent::Notification( + address.clone(), + Endpoint::Tx, + data, + )); + }, + Err(_) => break, + } + }, + _ = token.cancelled().fuse() => { + break; } - }, - _ = token.cancelled().fuse() => { - break; } } - } - info!("Data channel closed, ending websocket server device listener task"); - }); + info!("Data channel closed, ending websocket server device listener task"); + }, + info_span!("WebsocketServerHardware::subscribe_listener").or_current(), + ); Ok(()) } .boxed() diff --git a/crates/buttplug_server_hwmgr_xinput/Cargo.toml b/crates/buttplug_server_hwmgr_xinput/Cargo.toml index ded9f8459..3417f0546 100644 --- a/crates/buttplug_server_hwmgr_xinput/Cargo.toml +++ b/crates/buttplug_server_hwmgr_xinput/Cargo.toml @@ -20,7 +20,7 @@ doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } futures = "0.3.31" diff --git a/crates/buttplug_server_hwmgr_xinput/src/xinput_hardware.rs b/crates/buttplug_server_hwmgr_xinput/src/xinput_hardware.rs index 29d3a4fe3..b4619e695 100644 --- a/crates/buttplug_server_hwmgr_xinput/src/xinput_hardware.rs +++ b/crates/buttplug_server_hwmgr_xinput/src/xinput_hardware.rs @@ -33,6 +33,7 @@ use std::{ }; use tokio::sync::broadcast; use tokio_util::sync::CancellationToken; +use tracing::info_span; pub(super) fn create_address(index: XInputControllerIndex) -> String { index.to_string() @@ -55,7 +56,7 @@ async fn check_gamepad_connectivity( } tokio::select! { _ = cancellation_token.cancelled() => return, - _ = tokio::time::sleep(Duration::from_millis(500)) => continue + _ = async_manager::sleep(Duration::from_millis(500)) => continue } } } @@ -113,9 +114,12 @@ impl XInputHardware { let token = CancellationToken::new(); let child = token.child_token(); let sender = device_event_sender.clone(); - async_manager::spawn(async move { - check_gamepad_connectivity(index, sender, child).await; - }); + async_manager::spawn( + async move { + check_gamepad_connectivity(index, sender, child).await; + }, + info_span!("XInputHardware::check_gamepad_connectivity").or_current(), + ); Self { handle: rusty_xinput::XInputHandle::load_default().expect("The DLL should load as long as we're on windows, and we don't get here if we're not on windows."), index, diff --git a/crates/buttplug_tests/Cargo.toml b/crates/buttplug_tests/Cargo.toml index 29b1f971f..0e5ce9c33 100644 --- a/crates/buttplug_tests/Cargo.toml +++ b/crates/buttplug_tests/Cargo.toml @@ -16,13 +16,13 @@ buttplug_client = { version = "10.0.0", path = "../buttplug_client" } buttplug_client_in_process = { version = "10.0.0", path = "../buttplug_client_in_process", default-features = false} buttplug_server = { version = "10.0.0", path = "../buttplug_server" } buttplug_server_device_config = { version = "10.0.0", path = "../buttplug_server_device_config" } +buttplug_server_hwmgr_btleplug = { version = "10.0.0", path = "../buttplug_server_hwmgr_btleplug" } log = "0.4.29" tokio = { version = "1.49.0", features = ["macros"] } uuid = "1.20.0" futures = "0.3.31" tracing = "0.1.44" tracing-subscriber = "0.3.22" -tracing-futures = "0.2.5" tokio-test = "0.4.5" serde = "1.0.228" async-trait = "0.1.89" @@ -32,3 +32,5 @@ getset = "0.1.6" jsonschema = { version = "0.38.1", default-features = false } test-case = "3.3.1" serde_yaml = "0.9.34" +tabled = "0.20" +humansize = "2" diff --git a/crates/buttplug_tests/tests/test_memory.rs b/crates/buttplug_tests/tests/test_memory.rs new file mode 100644 index 000000000..25226cd51 --- /dev/null +++ b/crates/buttplug_tests/tests/test_memory.rs @@ -0,0 +1,415 @@ +use buttplug_server_device_config::load_protocol_configs; +use humansize::{BINARY, format_size}; +use std::alloc::System; +use std::cell::Cell as TlsCell; +use std::collections::HashMap; +use std::sync::{Mutex, OnceLock}; +use tabled::settings::object::Rows; +use tabled::settings::style::HorizontalLine; +use tabled::{ + builder::Builder, + settings::{ + Alignment, + Modify, + Style, + object::{Cell, Columns}, + span::ColumnSpan, + }, +}; +use tracing::{Level, span}; +use tracing_subscriber::Layer; +use tracing_subscriber::layer::SubscriberExt; + +/// Run `$body` inside a named TRACE span. +/// Sync expr: `in_span!("name", expr)` +/// Sync block: `in_span!("name", { ... })` +/// Async: `in_span!("name", async { ... }).await` +macro_rules! in_span { + ($name:expr, async $body:block) => {{ + use tracing::Instrument; + async $body.instrument(span!(Level::TRACE, $name)) + }}; + ($name:expr, { $($body:tt)* }) => {{ + let _span = span!(Level::TRACE, $name).entered(); + $($body)* + }}; + ($name:expr, $body:expr) => {{ + let _span = span!(Level::TRACE, $name).entered(); + $body + }}; +} + +#[derive(Debug, Clone)] +struct Stats { + alloc: i64, + dealloc: i64, +} + +impl Stats { + fn delta(&self) -> i64 { + self.alloc - self.dealloc + } +} + +impl std::ops::Add for Stats { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + alloc: self.alloc + rhs.alloc, + dealloc: self.dealloc + rhs.dealloc, + } + } +} + +// --------------------------------------------------------------------------- +// Global allocator with per-span memory tracking +// --------------------------------------------------------------------------- + +#[global_allocator] +static GLOBAL: Allocator = Allocator {}; + +struct Allocator; + +// Fixed-size span ID stack stored in TLS – no heap allocation needed. +const STACK_DEPTH: usize = 64; + +thread_local! { + /// Set to true while we are inside span-stat bookkeeping to prevent re-entrant tracking. + static TRACKING: TlsCell = TlsCell::new(false); + /// Stack of active span IDs (index 0 = oldest, depth-1 = innermost). + static SPAN_STACK: TlsCell<[u64; STACK_DEPTH]> = TlsCell::new([0u64; STACK_DEPTH]); + static SPAN_DEPTH: TlsCell = TlsCell::new(0); +} + +/// RAII guard that holds the TRACKING flag for its lifetime. +/// Obtain one via `TrackingGuard::acquire()`; returns `None` when already tracking. +struct TrackingGuard; + +impl TrackingGuard { + fn acquire() -> Option { + TRACKING.with(|t| { + if t.get() { + None + } else { + t.set(true); + Some(TrackingGuard) + } + }) + } +} + +impl Drop for TrackingGuard { + fn drop(&mut self) { + TRACKING.with(|t| t.set(false)); + } +} + +fn push_span(id: u64) { + SPAN_DEPTH.with(|d| { + let depth = d.get(); + if depth < STACK_DEPTH { + SPAN_STACK.with(|s| { + let mut arr = s.get(); + arr[depth] = id; + s.set(arr); + }); + } + d.set(depth + 1); + }); +} + +fn pop_span(id: u64) { + SPAN_DEPTH.with(|d| { + let depth = d.get(); + if depth > 0 { + SPAN_STACK.with(|s| { + let arr = s.get(); + if arr[depth - 1] == id { + d.set(depth - 1); + } + }); + } + }); +} + +fn current_span() -> Option { + SPAN_DEPTH.with(|d| { + let depth = d.get(); + if depth == 0 { + return None; + } + SPAN_STACK.with(|s| { + let id = s.get()[depth - 1]; + if id == 0 { None } else { Some(id) } + }) + }) +} + +// --------------------------------------------------------------------------- +// Per-span statistics +// --------------------------------------------------------------------------- + +struct SpanStats { + name: String, + parent: Option, + own: Stats, +} + +impl SpanStats { + fn total(reg: &SpanRegistry, id: u64) -> Stats { + let s = ®.stats[&id]; + let mut total = s.own.clone(); + for &child_id in ®.order { + if reg.stats[&child_id].parent == Some(id) { + total = total + SpanStats::total(reg, child_id); + } + } + total + } +} + +struct SpanRegistry { + stats: HashMap, + /// Insertion order – used when printing the tree. + order: Vec, +} + +impl SpanRegistry { + fn new() -> Self { + Self { + stats: HashMap::new(), + order: Vec::new(), + } + } +} + +static REGISTRY: OnceLock> = OnceLock::new(); + +fn registry() -> &'static Mutex { + REGISTRY.get_or_init(|| Mutex::new(SpanRegistry::new())) +} + +// --------------------------------------------------------------------------- +// GlobalAlloc impl +// --------------------------------------------------------------------------- + +unsafe impl std::alloc::GlobalAlloc for Allocator { + unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 { + let ptr = unsafe { System.alloc(layout) }; + + if let Some(_guard) = TrackingGuard::acquire() { + if let Some(span_id) = current_span() { + if let Ok(mut reg) = registry().try_lock() { + if let Some(s) = reg.stats.get_mut(&span_id) { + s.own.alloc += layout.size() as i64; + } + } + } + } + + ptr + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) { + unsafe { System.dealloc(ptr, layout) }; + + if let Some(_guard) = TrackingGuard::acquire() { + if let Some(span_id) = current_span() { + if let Ok(mut reg) = registry().try_lock() { + if let Some(s) = reg.stats.get_mut(&span_id) { + s.own.dealloc += layout.size() as i64; + } + } + } + } + } +} + +// --------------------------------------------------------------------------- +// Tracing layer – captures span lifecycle to drive the ID stack +// --------------------------------------------------------------------------- + +struct MemTrackingLayer; + +impl Layer for MemTrackingLayer { + fn on_new_span( + &self, + attrs: &span::Attributes<'_>, + id: &tracing::span::Id, + _ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + let span_id = id.clone().into_u64(); + let parent = current_span(); + + // Hold the guard so HashMap's own allocations aren't attributed anywhere. + let _guard = TrackingGuard::acquire(); + if let Ok(mut reg) = registry().try_lock() { + reg.order.push(span_id); + reg.stats.insert( + span_id, + SpanStats { + name: attrs.metadata().name().to_string(), + parent, + own: Stats { + alloc: 0, + dealloc: 0, + }, + }, + ); + } + } + + fn on_enter(&self, id: &tracing::span::Id, _ctx: tracing_subscriber::layer::Context<'_, S>) { + push_span(id.clone().into_u64()); + } + + fn on_exit(&self, id: &tracing::span::Id, _ctx: tracing_subscriber::layer::Context<'_, S>) { + pop_span(id.clone().into_u64()); + } +} + +// --------------------------------------------------------------------------- +// Tree printer +// --------------------------------------------------------------------------- + +fn format_bytes(bytes: i64) -> String { + format_size(bytes.unsigned_abs(), BINARY) +} + +fn signed_bytes(bytes: i64) -> String { + let sign = if bytes < 0 { "-" } else { "+" }; + format!("{}{}", sign, format_size(bytes.unsigned_abs(), BINARY)) +} + +fn collect_rows(reg: &SpanRegistry, parent: Option, depth: usize, builder: &mut Builder) { + for &id in ®.order { + let s = ®.stats[&id]; + if s.parent != parent { + continue; + } + let total = SpanStats::total(reg, id); + builder.push_record([ + format!("{}{}", " ".repeat(depth), s.name), + format_bytes(s.own.alloc), + format_bytes(s.own.dealloc), + signed_bytes(s.own.delta()), + format_bytes(total.alloc), + format_bytes(total.dealloc), + signed_bytes(total.delta()), + ]); + collect_rows(reg, Some(id), depth + 1, builder); + } +} + +fn print_tree(reg: &SpanRegistry) { + let mut builder = Builder::new(); + builder.push_record(["Span", "Own", "", "", "Total", "", ""]); + builder.push_record(["", "+", "-", "Δ", "+", "-", "Δ"]); + collect_rows(reg, None, 0, &mut builder); + + let table = builder + .build() + .with(Style::sharp().horizontals([(2, HorizontalLine::inherit(Style::modern()))])) + .with(Modify::new(Columns::new(1..)).with(Alignment::right())) + .with(Modify::new(Rows::new(0..2)).with(Alignment::center())) + .with(Modify::new(Columns::first()).with(Alignment::left())) + .with(Modify::new(Cell::new(0, 1)).with(ColumnSpan::new(3))) + .with(Modify::new(Cell::new(0, 4)).with(ColumnSpan::new(3))) + .to_string(); + println!("{table}"); +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[test] +fn test_memory() { + let subscriber = tracing_subscriber::registry().with(MemTrackingLayer); + let _guard = tracing::subscriber::set_default(subscriber); + + tokio::runtime::Builder::new_current_thread() + .enable_all() + .start_paused(true) + .build() + .unwrap() + .block_on(async { + let _client = memory_test().await; + + let reg = registry().lock().unwrap(); + print_tree(®); + }); +} + +async fn memory_test() -> buttplug_client::ButtplugClient { + in_span!("memory_test", { + let dcm = in_span!("DeviceConfigurationManager", { + let builder = in_span!( + "load_protocol_configs", + load_protocol_configs(&None, &None, false).unwrap() + ); + let dcm = in_span!( + "DeviceConfigurationManagerBuilder.finish", + builder.finish().unwrap() + ); + dcm + }); + + let manager = in_span!("ServerDeviceManager", { + let mut builder = in_span!( + "ServerDeviceManagerBuilder::new", + buttplug_server::device::ServerDeviceManagerBuilder::new(dcm) + ); + + in_span!( + "builder.comm_manager(BtlePlugCommunicationManagerBuilder)", + builder.comm_manager( + buttplug_server_hwmgr_btleplug::BtlePlugCommunicationManagerBuilder::default() + ) + ); + + in_span!( + "ServerDeviceManagerBuilder.finish", + builder.finish().unwrap() + ) + }); + + let server = in_span!("ButtplugServerBuilder", { + let builder = buttplug_server::ButtplugServerBuilder::new(manager); + + in_span!("ButtplugServerBuilder.finish", builder.finish().unwrap()) + }); + + let connector = in_span!("ButtplugClientInProcessConnector", { + let mut builder = in_span!( + "ButtplugInProcessClientConnectorBuilder::default", + buttplug_client_in_process::ButtplugInProcessClientConnectorBuilder::default() + ); + + in_span!( + "ButtplugClientInProcessConnectorBuilder.server", + builder.server(server) + ); + + in_span!( + "ButtplugInProcessClientConnectorBuilder.finish", + builder.finish() + ) + }); + + in_span!("ButtplugClient", { + let client = in_span!( + "ButtplugClient::new", + buttplug_client::ButtplugClient::new("Memory Test Client") + ); + + in_span!( + "ButtplugClient.connect", + client.connect(connector).await.unwrap() + ); + + client + }) + }) +} diff --git a/crates/buttplug_tests/tests/test_serializers.rs b/crates/buttplug_tests/tests/test_serializers.rs index 906f84ec2..5e424526e 100644 --- a/crates/buttplug_tests/tests/test_serializers.rs +++ b/crates/buttplug_tests/tests/test_serializers.rs @@ -29,6 +29,7 @@ use buttplug_server::message::{ }; use std::sync::Arc; use tokio::sync::Notify; +use tracing::info_span; use util::channel_transport::ChannelClientTestHelper; #[tokio::test] @@ -38,13 +39,16 @@ async fn test_garbled_client_rsi_response() { let helper_clone = helper.clone(); let finish_notifier = Arc::new(Notify::new()); let finish_notifier_clone = finish_notifier.clone(); - async_manager::spawn(async move { - helper_clone - .connect_without_reply() - .await - .expect("Test, assuming infallible."); - finish_notifier_clone.notify_waiters(); - }); + async_manager::spawn( + async move { + helper_clone + .connect_without_reply() + .await + .expect("Test, assuming infallible."); + finish_notifier_clone.notify_waiters(); + }, + info_span!("ChannelClientTestHelper::simulate_successful_connect").or_current(), + ); // Just assume we get an RSI message let _ = helper.recv_outgoing().await; // Send back crap. @@ -72,19 +76,22 @@ async fn test_serialized_error_relay() { let helper = Arc::new(ChannelClientTestHelper::new()); helper.simulate_successful_connect().await; let helper_clone = helper.clone(); - async_manager::spawn(async move { - assert!(matches!( - helper_clone.next_client_message().await, - ButtplugClientMessageVariant::V4(ButtplugClientMessageV4::StartScanning(..)) - )); - let mut error_msg = ButtplugServerMessageV4::Error(ErrorV0::from(ButtplugError::from( - ButtplugUnknownError::NoDeviceCommManagers, - ))); - error_msg.set_id(3); - helper_clone - .send_client_incoming(ButtplugServerMessageVariant::V4(error_msg)) - .await; - }); + async_manager::spawn( + async move { + assert!(matches!( + helper_clone.next_client_message().await, + ButtplugClientMessageVariant::V4(ButtplugClientMessageV4::StartScanning(..)) + )); + let mut error_msg = ButtplugServerMessageV4::Error(ErrorV0::from(ButtplugError::from( + ButtplugUnknownError::NoDeviceCommManagers, + ))); + error_msg.set_id(3); + helper_clone + .send_client_incoming(ButtplugServerMessageVariant::V4(error_msg)) + .await; + }, + info_span!("test_serialized_error_relay::client_message_sender").or_current(), + ); assert!(matches!( helper.client().start_scanning().await.unwrap_err(), ButtplugClientError::ButtplugError(ButtplugError::ButtplugUnknownError( diff --git a/crates/buttplug_tests/tests/util/channel_transport.rs b/crates/buttplug_tests/tests/util/channel_transport.rs index f38cdcad5..8b7a4e7b8 100644 --- a/crates/buttplug_tests/tests/util/channel_transport.rs +++ b/crates/buttplug_tests/tests/util/channel_transport.rs @@ -51,6 +51,7 @@ use tokio::sync::{ Notify, mpsc::{Receiver, Sender, channel}, }; +use tracing::info_span; struct ChannelTransport { outside_receiver: Arc>>>, @@ -80,37 +81,40 @@ impl ButtplugConnectorTransport for ChannelTransport { let disconnect_notifier = self.disconnect_notifier.clone(); let outside_sender = self.outside_sender.clone(); let outside_receiver_mutex = self.outside_receiver.clone(); - async_manager::spawn(async move { - let mut outside_receiver = outside_receiver_mutex - .lock() - .await - .take() - .expect("Test, assuming infallible"); - loop { - select! { - _ = disconnect_notifier.notified().fuse() => { - info!("Test requested disconnect."); - return; - } - outgoing = outgoing_receiver.recv().fuse() => { - if let Some(o) = outgoing { - outside_sender.send(o).await.expect("Test, assuming infallible"); - } else { - info!("Test dropped stream, returning"); + async_manager::spawn( + async move { + let mut outside_receiver = outside_receiver_mutex + .lock() + .await + .take() + .expect("Test, assuming infallible"); + loop { + select! { + _ = disconnect_notifier.notified().fuse() => { + info!("Test requested disconnect."); return; } - } - incoming = outside_receiver.recv().fuse() => { - if let Some(i) = incoming { - incoming_sender.send(i).await.expect("Test, assuming infallible"); - } else { - info!("Test dropped stream, returning"); - return; + outgoing = outgoing_receiver.recv().fuse() => { + if let Some(o) = outgoing { + outside_sender.send(o).await.expect("Test, assuming infallible"); + } else { + info!("Test dropped stream, returning"); + return; + } + } + incoming = outside_receiver.recv().fuse() => { + if let Some(i) = incoming { + incoming_sender.send(i).await.expect("Test, assuming infallible"); + } else { + info!("Test dropped stream, returning"); + return; + } } - } - }; - } - }); + }; + } + }, + info_span!("ChannelTransport::connect_loop").or_current(), + ); future::ready(Ok(())).boxed() } @@ -191,12 +195,15 @@ impl ChannelClientTestHelper { .expect("Test, assuming infallible"); let finish_notifier = Arc::new(Notify::new()); let finish_notifier_clone = finish_notifier.clone(); - async_manager::spawn(async move { - if let Err(e) = client_clone.connect(connector).await { - assert!(false, "Error connecting to client: {:?}", e); - } - finish_notifier_clone.notify_waiters(); - }); + async_manager::spawn( + async move { + if let Err(e) = client_clone.connect(connector).await { + assert!(false, "Error connecting to client: {:?}", e); + } + finish_notifier_clone.notify_waiters(); + }, + info_span!("ChannelClientTestHelper::simulate_successful_connect").or_current(), + ); // Wait for RequestServerInfo message assert!(matches!( self.next_client_message().await, diff --git a/crates/buttplug_tests/tests/util/device_test/client/client_v2/client.rs b/crates/buttplug_tests/tests/util/device_test/client/client_v2/client.rs index 5bc54712b..47af643af 100644 --- a/crates/buttplug_tests/tests/util/device_test/client/client_v2/client.rs +++ b/crates/buttplug_tests/tests/util/device_test/client/client_v2/client.rs @@ -20,7 +20,6 @@ use buttplug_core::{ }, util::{async_manager, stream::convert_broadcast_receiver_to_stream}, }; -use futures::channel::oneshot; use buttplug_server::message::{ ButtplugClientMessageV2, ButtplugServerMessageV2, @@ -28,6 +27,7 @@ use buttplug_server::message::{ StopAllDevicesV0, }; use dashmap::DashMap; +use futures::channel::oneshot; use futures::{ Stream, future::{self, BoxFuture}, @@ -39,8 +39,7 @@ use std::sync::{ }; use thiserror::Error; use tokio::sync::{Mutex, broadcast, mpsc, mpsc::error::SendError}; -use tracing::{Level, Span, span}; -use tracing_futures::Instrument; +use tracing::{Level, Span, info_span, span}; /// Result type used for public APIs. /// @@ -168,15 +167,18 @@ impl ButtplugClient { pub fn new(name: &str) -> (Self, mpsc::Receiver) { let (message_sender, message_receiver) = mpsc::channel(256); let (event_stream, _) = broadcast::channel(256); - (Self { - client_name: name.to_owned(), - server_name: Arc::new(Mutex::new(None)), - event_stream, - message_sender, - _client_span: Arc::new(Mutex::new(None)), - connected: Arc::new(AtomicBool::new(false)), - device_map: Arc::new(DashMap::new()), - }, message_receiver) + ( + Self { + client_name: name.to_owned(), + server_name: Arc::new(Mutex::new(None)), + event_stream, + message_sender, + _client_span: Arc::new(Mutex::new(None)), + connected: Arc::new(AtomicBool::new(false)), + device_map: Arc::new(DashMap::new()), + }, + message_receiver, + ) } pub async fn connect( @@ -220,8 +222,8 @@ impl ButtplugClient { async_manager::spawn( async move { client_event_loop.run().await; - } - .instrument(tracing::info_span!("Client Loop Span")), + }, + info_span!("ClientLoop").or_current(), ); self.run_handshake().await } diff --git a/crates/buttplug_tests/tests/util/device_test/client/client_v2/device.rs b/crates/buttplug_tests/tests/util/device_test/client/client_v2/device.rs index aab576acc..6de1fe9be 100644 --- a/crates/buttplug_tests/tests/util/device_test/client/client_v2/device.rs +++ b/crates/buttplug_tests/tests/util/device_test/client/client_v2/device.rs @@ -40,6 +40,7 @@ use buttplug_server::message::{ use futures::{Stream, future}; use getset::Getters; use log::*; +use tracing::Instrument; use std::{ collections::HashMap, fmt, @@ -49,7 +50,6 @@ use std::{ }, }; use tokio::sync::{broadcast, mpsc}; -use tracing_futures::Instrument; /// Enum for messages going to a [ButtplugClientDevice] instance. #[derive(Clone, Debug)] diff --git a/crates/buttplug_tests/tests/util/device_test/client/client_v2/in_process_connector.rs b/crates/buttplug_tests/tests/util/device_test/client/client_v2/in_process_connector.rs index 7138d18cd..63a3f7b13 100644 --- a/crates/buttplug_tests/tests/util/device_test/client/client_v2/in_process_connector.rs +++ b/crates/buttplug_tests/tests/util/device_test/client/client_v2/in_process_connector.rs @@ -28,7 +28,6 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, }; use tokio::sync::mpsc::{Sender, channel}; -use tracing_futures::Instrument; #[derive(Default)] pub struct ButtplugInProcessClientConnectorBuilder { @@ -133,7 +132,7 @@ impl ButtplugConnector } } info!("Stopping In Process Client Connector Event Sender Loop, due to channel receiver being dropped."); - }.instrument(tracing::info_span!("InProcessClientConnectorEventSenderLoop"))); + }, tracing::info_span!("InProcessClientConnectorEventSenderLoop")); connected.store(true, Ordering::Relaxed); Ok(()) }.boxed() diff --git a/crates/buttplug_tests/tests/util/device_test/client/client_v2/mod.rs b/crates/buttplug_tests/tests/util/device_test/client/client_v2/mod.rs index 1a738edf0..6b15003e1 100644 --- a/crates/buttplug_tests/tests/util/device_test/client/client_v2/mod.rs +++ b/crates/buttplug_tests/tests/util/device_test/client/client_v2/mod.rs @@ -24,6 +24,7 @@ use client::{ButtplugClient, ButtplugClientEvent}; use device::{ButtplugClientDevice, LinearCommand, RotateCommand, VibrateCommand}; use in_process_connector::ButtplugInProcessClientConnectorBuilder; use tokio::sync::Notify; +use tracing::info_span; use super::super::{ super::TestDeviceCommunicationManagerBuilder, @@ -78,10 +79,13 @@ async fn run_test_client_command(command: &TestClientCommand, device: &Arc, - connected: &Arc, - ) -> Self { + fn new(message_sender: mpsc::Sender, connected: &Arc) -> Self { Self { message_sender, connected: connected.clone(), @@ -248,17 +245,17 @@ impl ButtplugClient { let (message_sender, message_receiver) = mpsc::channel(256); let (event_stream, _) = broadcast::channel(256); let connected = Arc::new(AtomicBool::new(false)); - (Self { - client_name: name.to_owned(), - server_name: Arc::new(Mutex::new(None)), - event_stream, - message_sender: Arc::new(ButtplugClientMessageSender::new( - message_sender, - &connected, - )), - connected, - device_map: Arc::new(DashMap::new()), - }, message_receiver) + ( + Self { + client_name: name.to_owned(), + server_name: Arc::new(Mutex::new(None)), + event_stream, + message_sender: Arc::new(ButtplugClientMessageSender::new(message_sender, &connected)), + connected, + device_map: Arc::new(DashMap::new()), + }, + message_receiver, + ) } pub async fn connect( @@ -299,8 +296,8 @@ impl ButtplugClient { async_manager::spawn( async move { client_event_loop.run().await; - } - .instrument(tracing::info_span!("Client Loop Span")), + }, + info_span!("ClientLoop").or_current(), ); self.run_handshake().await } diff --git a/crates/buttplug_tests/tests/util/device_test/client/client_v3/connector/in_process_connector.rs b/crates/buttplug_tests/tests/util/device_test/client/client_v3/connector/in_process_connector.rs index 3c47feb32..2a54b5e24 100644 --- a/crates/buttplug_tests/tests/util/device_test/client/client_v3/connector/in_process_connector.rs +++ b/crates/buttplug_tests/tests/util/device_test/client/client_v3/connector/in_process_connector.rs @@ -28,7 +28,6 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, }; use tokio::sync::mpsc::{Sender, channel}; -use tracing_futures::Instrument; #[derive(Default)] pub struct ButtplugInProcessClientConnectorBuilder { @@ -131,7 +130,7 @@ impl ButtplugConnector } } info!("Stopping In Process Client Connector Event Sender Loop, due to channel receiver being dropped."); - }.instrument(tracing::info_span!("InProcessClientConnectorEventSenderLoop"))); + }, tracing::info_span!("InProcessClientConnectorEventSenderLoop")); connected.store(true, Ordering::Relaxed); Ok(()) }.boxed() diff --git a/crates/buttplug_tests/tests/util/device_test/client/client_v3/mod.rs b/crates/buttplug_tests/tests/util/device_test/client/client_v3/mod.rs index 8ef64567b..0b6366a7d 100644 --- a/crates/buttplug_tests/tests/util/device_test/client/client_v3/mod.rs +++ b/crates/buttplug_tests/tests/util/device_test/client/client_v3/mod.rs @@ -25,6 +25,7 @@ use buttplug_core::util::async_manager; use buttplug_server::{ButtplugServer, ButtplugServerBuilder, device::ServerDeviceManagerBuilder}; use buttplug_server_device_config::load_protocol_configs; use tokio::sync::Notify; +use tracing::info_span; use super::super::{ super::TestDeviceCommunicationManagerBuilder, @@ -89,10 +90,13 @@ async fn run_test_client_command(command: &TestClientCommand, device: &Arc &u32 { || fo .rotate() .as_ref() - .is_some_and(|r| r.step_limit().start() >= &0) + .is_some_and(|r| r.step_limit().start() >= 0) }); while let Some((idx, _)) = iter.next() { if offset >= index { @@ -131,9 +132,11 @@ async fn run_test_client_command(command: &TestClientCommand, device: &ButtplugC .map(|(_, x)| x) .collect(); let f = rotate_features[cmd.index() as usize].clone(); - f.run_output(&ClientDeviceOutputCommand::Rotate(ClientDeviceCommandValue::Percent( - cmd.speed() * if cmd.clockwise() { 1f64 } else { -1f64 }, - ))) + f.run_output(&ClientDeviceOutputCommand::Rotate( + ClientDeviceCommandValue::Percent( + cmd.speed() * if cmd.clockwise() { 1f64 } else { -1f64 }, + ), + )) }) .collect(); futures::future::try_join_all(fut_vec).await.unwrap(); @@ -153,7 +156,8 @@ async fn run_test_client_command(command: &TestClientCommand, device: &ButtplugC .get(OutputType::HwPositionWithDuration) .unwrap() .step_count() as f64) - .ceil() as u32).into(), + .ceil() as u32) + .into(), cmd.duration(), )) }) @@ -169,10 +173,13 @@ async fn run_test_client_command(command: &TestClientCommand, device: &ButtplugC // their notification endpoint. This is a mess but it does the job. let device = device.clone(); let expected_power = *expected_power; - async_manager::spawn(async move { - let battery_level = device.battery().await.unwrap() as f64 / 100f64; - assert_eq!(battery_level, expected_power); - }); + async_manager::spawn( + async move { + let battery_level = device.battery().await.unwrap() as f64 / 100f64; + assert_eq!(battery_level, expected_power); + }, + info_span!("BatteryLevelCheck").or_current(), + ); } else { assert_eq!( device.battery().await.unwrap() as f64 / 100f64, @@ -264,12 +271,15 @@ pub async fn run_json_test_case(test_case: &DeviceTestCase) { let (server, device_channels) = build_server(test_case); let remote_server = ButtplugTestServer::new(server); - async_manager::spawn(async move { - remote_server - .start(server_connector) - .await - .expect("Should always succeed"); - }); + async_manager::spawn( + async move { + remote_server + .start(server_connector) + .await + .expect("Should always succeed"); + }, + info_span!("RemoteServer").or_current(), + ); // Connect client let client = ButtplugClient::new("Test Client"); diff --git a/crates/buttplug_tests/tests/util/device_test/connector/channel_transport.rs b/crates/buttplug_tests/tests/util/device_test/connector/channel_transport.rs index d804e1a3a..6c2c79530 100644 --- a/crates/buttplug_tests/tests/util/device_test/connector/channel_transport.rs +++ b/crates/buttplug_tests/tests/util/device_test/connector/channel_transport.rs @@ -24,6 +24,7 @@ use tokio::{ mpsc::{Receiver, Sender}, }, }; +use tracing::info_span; pub struct ChannelTransport { external_sender: Sender, @@ -78,7 +79,7 @@ impl ButtplugConnectorTransport for ChannelTransport { } }; } - }); + }, info_span!("ChannelTransport::message_loop").or_current()); Ok(()) } .boxed() diff --git a/crates/buttplug_tests/tests/util/test_device_manager/test_device.rs b/crates/buttplug_tests/tests/util/test_device_manager/test_device.rs index 2fd9984a3..de15c43a2 100644 --- a/crates/buttplug_tests/tests/util/test_device_manager/test_device.rs +++ b/crates/buttplug_tests/tests/util/test_device_manager/test_device.rs @@ -32,6 +32,7 @@ use std::{ time::Duration, }; use tokio::sync::{Mutex, broadcast, mpsc}; +use tracing::info_span; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TestHardwareNotification { @@ -175,36 +176,39 @@ impl TestDevice { let subscribed_endpoints_clone = subscribed_endpoints.clone(); let read_data = Arc::new(Mutex::new(VecDeque::new())); let read_data_clone = read_data.clone(); - async_manager::spawn(async move { - while let Some(event) = receiver.recv().await { - match event { - TestHardwareEvent::Disconnect => { - event_sender_clone - .send(HardwareEvent::Disconnected(address_clone.clone())) - .expect("Test"); - } - TestHardwareEvent::Notifications(notifications) => { - for notification in notifications { - if subscribed_endpoints_clone.contains(¬ification.endpoint) { - event_sender_clone - .send(HardwareEvent::Notification( - address_clone.clone(), - notification.endpoint, - notification.data.clone(), - )) - .expect("Test"); + async_manager::spawn( + async move { + while let Some(event) = receiver.recv().await { + match event { + TestHardwareEvent::Disconnect => { + event_sender_clone + .send(HardwareEvent::Disconnected(address_clone.clone())) + .expect("Test"); + } + TestHardwareEvent::Notifications(notifications) => { + for notification in notifications { + if subscribed_endpoints_clone.contains(¬ification.endpoint) { + event_sender_clone + .send(HardwareEvent::Notification( + address_clone.clone(), + notification.endpoint, + notification.data.clone(), + )) + .expect("Test"); + } } } - } - TestHardwareEvent::Reads(events) => { - let mut guard = read_data_clone.lock().await; - for read in events { - guard.push_front(HardwareReading::new(read.endpoint, &read.data)); + TestHardwareEvent::Reads(events) => { + let mut guard = read_data_clone.lock().await; + for read in events { + guard.push_front(HardwareReading::new(read.endpoint, &read.data)); + } } } } - } - }); + }, + info_span!("TestDevice::command_loop").or_current(), + ); Self { name: name.to_owned(), diff --git a/crates/buttplug_tests/tests/util/test_server.rs b/crates/buttplug_tests/tests/util/test_server.rs index d8056d3f5..55c3a2342 100644 --- a/crates/buttplug_tests/tests/util/test_server.rs +++ b/crates/buttplug_tests/tests/util/test_server.rs @@ -21,6 +21,7 @@ use log::*; use std::sync::Arc; use thiserror::Error; use tokio::sync::{Notify, mpsc}; +use tracing::info_span; #[derive(Error, Debug)] pub enum ButtplugServerConnectorError { @@ -57,27 +58,30 @@ async fn run_server( trace!("Got message from connector: {:?}", client_message); let server_clone = server.clone(); let connector_clone = shared_connector.clone(); - async_manager::spawn(async move { - if let Err(e) = client_message.is_valid() { - error!("Message not valid: {:?} - Error: {}", client_message, e); - let mut err_msg = ErrorV0::from(ButtplugError::from(e)); - err_msg.set_id(client_message.id()); - let _ = connector_clone.send(ButtplugServerMessageVariant::V3(err_msg.into())).await; - return; - } - match server_clone.parse_message(client_message.clone()).await { - Ok(ret_msg) => { - if connector_clone.send(ret_msg).await.is_err() { - error!("Cannot send reply to server, dropping and assuming remote server thread has exited."); - } - }, - Err(err_msg) => { - if connector_clone.send(err_msg).await.is_err() { - error!("Cannot send reply to server, dropping and assuming remote server thread has exited."); + async_manager::spawn( + async move { + if let Err(e) = client_message.is_valid() { + error!("Message not valid: {:?} - Error: {}", client_message, e); + let mut err_msg = ErrorV0::from(ButtplugError::from(e)); + err_msg.set_id(client_message.id()); + let _ = connector_clone.send(ButtplugServerMessageVariant::V3(err_msg.into())).await; + return; + } + match server_clone.parse_message(client_message.clone()).await { + Ok(ret_msg) => { + if connector_clone.send(ret_msg).await.is_err() { + error!("Cannot send reply to server, dropping and assuming remote server thread has exited."); + } + }, + Err(err_msg) => { + if connector_clone.send(err_msg).await.is_err() { + error!("Cannot send reply to server, dropping and assuming remote server thread has exited."); + } } } - } - }); + }, + info_span!("RemoteServerMessageHandler").or_current() + ); } }, _ = disconnect_notifier.notified().fuse() => { diff --git a/crates/buttplug_transport_websocket_tungstenite/Cargo.toml b/crates/buttplug_transport_websocket_tungstenite/Cargo.toml index efc0977a4..f4602f26c 100644 --- a/crates/buttplug_transport_websocket_tungstenite/Cargo.toml +++ b/crates/buttplug_transport_websocket_tungstenite/Cargo.toml @@ -20,14 +20,12 @@ doc = true [dependencies] -buttplug_core = { version = "10.0.0", path = "../buttplug_core" } +buttplug_core = { version = "10.0.0", path = "../buttplug_core", default-features = false } futures = "0.3.31" futures-util = "0.3.31" 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" dashmap = { version = "6.1.0", features = ["serde"] } log = "0.4.29" getset = "0.1.6" diff --git a/crates/buttplug_transport_websocket_tungstenite/src/websocket_client.rs b/crates/buttplug_transport_websocket_tungstenite/src/websocket_client.rs index d5dba5444..e5b0940ad 100644 --- a/crates/buttplug_transport_websocket_tungstenite/src/websocket_client.rs +++ b/crates/buttplug_transport_websocket_tungstenite/src/websocket_client.rs @@ -40,7 +40,7 @@ use tokio_tungstenite::{ connect_async_tls_with_config, tungstenite::protocol::Message, }; -use tracing::Instrument; +use tracing::info_span; use url::Url; pub fn get_rustls_config_dangerous() -> ClientConfig { @@ -287,8 +287,8 @@ impl ButtplugConnectorTransport for ButtplugWebsocketClientTransport { } } } - } - .instrument(tracing::info_span!("Websocket Client I/O Task")), + }, + info_span!("Websocket Client I/O Task").or_current(), ); Ok(()) } diff --git a/crates/buttplug_transport_websocket_tungstenite/src/websocket_server.rs b/crates/buttplug_transport_websocket_tungstenite/src/websocket_server.rs index 0c2addbb3..510493fce 100644 --- a/crates/buttplug_transport_websocket_tungstenite/src/websocket_server.rs +++ b/crates/buttplug_transport_websocket_tungstenite/src/websocket_server.rs @@ -19,6 +19,7 @@ use buttplug_core::{ util::async_manager, }; use futures::{FutureExt, SinkExt, StreamExt, future::BoxFuture}; +use tracing::info_span; use std::{sync::Arc, time::Duration}; use tokio::{ net::{TcpListener, TcpStream}, @@ -245,7 +246,7 @@ impl ButtplugConnectorTransport for ButtplugWebsocketServerTransport { disconnect_notifier_clone, ) .await; - }); + }, info_span!("ButtplugWebsocketServerTransport::connect").or_current()); Ok(()) } else { Err(ButtplugConnectorError::ConnectorGenericError( diff --git a/crates/intiface_engine/src/remote_server.rs b/crates/intiface_engine/src/remote_server.rs index a619e336b..1dc4212a9 100644 --- a/crates/intiface_engine/src/remote_server.rs +++ b/crates/intiface_engine/src/remote_server.rs @@ -86,7 +86,7 @@ async fn run_device_event_stream( index: da.1.device_index(), name: da.1.device_name().clone(), identifier: device_info.identifier().clone(), - display_name: device_info.display_name().clone(), + display_name: device_info.display_name().as_ref().map(|s| s.to_string()) }; if remote_event_sender.send(added_event).is_err() { error!( @@ -169,7 +169,7 @@ async fn run_server( } } } - }); + }, tracing::info_span!("RemoteServerMessageHandler").or_current()); } }, _ = disconnect_notifier.notified().fuse() => { diff --git a/examples/src/bin/device_tester.rs b/examples/src/bin/device_tester.rs index 0480560e7..87cffa402 100644 --- a/examples/src/bin/device_tester.rs +++ b/examples/src/bin/device_tester.rs @@ -104,7 +104,7 @@ async fn device_tester() { feature.feature_index() ); } else if let Some(out) = outs.get(OutputType::Rotate) { - cmds.push(feature.run_output(&ClientDeviceOutputCommand::Rotate((*out.step_limit().end()).into()))); + cmds.push(feature.run_output(&ClientDeviceOutputCommand::Rotate((out.step_limit().end()).into()))); println!( "{} ({}) should start rotating on feature {}!", dev.name(), @@ -129,7 +129,7 @@ async fn device_tester() { ); } else if let Some(out) = outs.get(OutputType::Temperature) { cmds.push( - feature.run_output(&ClientDeviceOutputCommand::Temperature((*out.step_limit().end()).into())), + feature.run_output(&ClientDeviceOutputCommand::Temperature((out.step_limit().end()).into())), ); println!( "{} ({}) should start heating on feature {}!", @@ -259,7 +259,7 @@ async fn device_tester() { ); } OutputType::Rotate => { - if output.step_limit().start() >= &0 { + if output.step_limit().start() >= 0 { set_level_and_wait(&dev, feature, &otype, 0.25).await; set_level_and_wait(&dev, feature, &otype, 0.5).await; set_level_and_wait(&dev, feature, &otype, 0.75).await;