Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8use crate::message::{
9 v1::NullDeviceMessageAttributesV1,
10 v2::{ClientDeviceMessageAttributesV2, GenericDeviceMessageAttributesV2},
11};
12use buttplug_core::message::{
13 DeviceFeature,
14 DeviceFeatureOutputValueProperties,
15 InputCommandType,
16 InputType,
17 OutputType,
18};
19use getset::{Getters, MutGetters, Setters};
20use serde::{Deserialize, Serialize, Serializer, ser::SerializeSeq};
21use std::ops::RangeInclusive;
22
23// This will look almost exactly like ServerDeviceMessageAttributes. However, it will only contain
24// information we want the client to know, i.e. step counts versus specific step ranges. This is
25// what will be sent to the client as part of DeviceAdded/DeviceList messages. It should not be used
26// for outside configuration/serialization, rather it should be a subset of that information.
27//
28// For many messages, client and server configurations may be exactly the same. If they are not,
29// then we denote this by prefixing the type with Client/Server. Server attributes will usually be
30// hosted in the server/device/configuration module.
31#[derive(Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
32pub struct ClientDeviceMessageAttributesV3 {
33 // Generic commands
34 #[getset(get = "pub", get_mut = "pub(super)")]
35 #[serde(rename = "ScalarCmd")]
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub(in crate::message) scalar_cmd: Option<Vec<ClientGenericDeviceMessageAttributesV3>>,
38 #[getset(get = "pub", get_mut = "pub(super)")]
39 #[serde(rename = "RotateCmd")]
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub(in crate::message) rotate_cmd: Option<Vec<ClientGenericDeviceMessageAttributesV3>>,
42 #[getset(get = "pub", get_mut = "pub(super)")]
43 #[serde(rename = "LinearCmd")]
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub(in crate::message) linear_cmd: Option<Vec<ClientGenericDeviceMessageAttributesV3>>,
46
47 // Sensor Messages
48 #[getset(get = "pub")]
49 #[serde(rename = "SensorReadCmd")]
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub(in crate::message) sensor_read_cmd: Option<Vec<SensorDeviceMessageAttributesV3>>,
52 #[getset(get = "pub")]
53 #[serde(rename = "SensorSubscribeCmd")]
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub(in crate::message) sensor_subscribe_cmd: Option<Vec<SensorDeviceMessageAttributesV3>>,
56
57 // StopDeviceCmd always exists
58 #[getset(get = "pub")]
59 #[serde(rename = "StopDeviceCmd")]
60 #[serde(skip_deserializing)]
61 pub(in crate::message) stop_device_cmd: NullDeviceMessageAttributesV1,
62
63 // Needed to load from config for fallback, but unused here.
64 #[getset(get = "pub")]
65 #[serde(rename = "FleshlightLaunchFW12Cmd")]
66 #[serde(skip_serializing)]
67 pub(in crate::message) fleshlight_launch_fw12_cmd: Option<NullDeviceMessageAttributesV1>,
68 #[getset(get = "pub")]
69 #[serde(rename = "VorzeA10CycloneCmd")]
70 #[serde(skip_serializing)]
71 pub(in crate::message) vorze_a10_cyclone_cmd: Option<NullDeviceMessageAttributesV1>,
72}
73
74pub fn vibrate_cmd_from_scalar_cmd(
75 attributes_vec: &[ClientGenericDeviceMessageAttributesV3],
76) -> GenericDeviceMessageAttributesV2 {
77 let mut feature_count = 0u32;
78 let mut step_count = vec![];
79 for attr in attributes_vec {
80 if *attr.actuator_type() == OutputType::Vibrate {
81 feature_count += 1;
82 step_count.push(*attr.step_count());
83 }
84 }
85 GenericDeviceMessageAttributesV2 {
86 feature_count,
87 step_count,
88 }
89}
90
91impl From<ClientDeviceMessageAttributesV3> for ClientDeviceMessageAttributesV2 {
92 fn from(other: ClientDeviceMessageAttributesV3) -> Self {
93 Self {
94 vibrate_cmd: other
95 .scalar_cmd()
96 .as_ref()
97 .map(|x| vibrate_cmd_from_scalar_cmd(x))
98 .filter(|x| x.feature_count() != 0),
99 rotate_cmd: other
100 .rotate_cmd()
101 .as_ref()
102 .map(|x| GenericDeviceMessageAttributesV2::from(x.clone())),
103 linear_cmd: other
104 .linear_cmd()
105 .as_ref()
106 .map(|x| GenericDeviceMessageAttributesV2::from(x.clone())),
107 battery_level_cmd: {
108 if let Some(sensor_info) = other.sensor_read_cmd() {
109 if sensor_info
110 .iter()
111 .any(|x| *x.sensor_type() == InputType::Battery)
112 {
113 Some(NullDeviceMessageAttributesV1::default())
114 } else {
115 None
116 }
117 } else {
118 None
119 }
120 },
121 rssi_level_cmd: {
122 if let Some(sensor_info) = other.sensor_read_cmd() {
123 if sensor_info
124 .iter()
125 .any(|x| *x.sensor_type() == InputType::Rssi)
126 {
127 Some(NullDeviceMessageAttributesV1::default())
128 } else {
129 None
130 }
131 } else {
132 None
133 }
134 },
135 stop_device_cmd: other.stop_device_cmd().clone(),
136 fleshlight_launch_fw12_cmd: other.fleshlight_launch_fw12_cmd().clone(),
137 vorze_a10_cyclone_cmd: other.vorze_a10_cyclone_cmd().clone(),
138 }
139 }
140}
141
142impl ClientDeviceMessageAttributesV3 {
143 pub fn finalize(&mut self) {
144 if let Some(scalar_attrs) = &mut self.scalar_cmd {
145 for (i, attr) in scalar_attrs.iter_mut().enumerate() {
146 attr.index = i as u32;
147 }
148 }
149 if let Some(sensor_read_attrs) = &mut self.sensor_read_cmd {
150 for (i, attr) in sensor_read_attrs.iter_mut().enumerate() {
151 attr.index = i as u32;
152 }
153 }
154 if let Some(sensor_subscribe_attrs) = &mut self.sensor_subscribe_cmd {
155 for (i, attr) in sensor_subscribe_attrs.iter_mut().enumerate() {
156 attr.index = i as u32;
157 }
158 }
159 }
160}
161
162fn unspecified_feature() -> String {
163 "N/A".to_string()
164}
165
166#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Getters, Setters)]
167pub struct ClientGenericDeviceMessageAttributesV3 {
168 #[getset(get = "pub")]
169 #[serde(rename = "FeatureDescriptor")]
170 #[serde(default = "unspecified_feature")]
171 pub(in crate::message) feature_descriptor: String,
172 #[getset(get = "pub")]
173 #[serde(rename = "ActuatorType")]
174 pub(in crate::message) actuator_type: OutputType,
175 #[serde(rename = "StepCount")]
176 #[getset(get = "pub")]
177 pub(in crate::message) step_count: u32,
178 // TODO This needs to actually be part of the device info relayed to the client in spec v4.
179 #[getset(get = "pub")]
180 #[serde(skip, default)]
181 pub(in crate::message) index: u32,
182}
183
184impl From<Vec<ClientGenericDeviceMessageAttributesV3>> for GenericDeviceMessageAttributesV2 {
185 fn from(attributes_vec: Vec<ClientGenericDeviceMessageAttributesV3>) -> Self {
186 Self {
187 feature_count: attributes_vec.len() as u32,
188 step_count: attributes_vec.iter().map(|x| *x.step_count()).collect(),
189 }
190 }
191}
192
193impl ClientGenericDeviceMessageAttributesV3 {
194 pub fn new(feature_descriptor: &str, step_count: u32, actuator_type: OutputType) -> Self {
195 Self {
196 feature_descriptor: feature_descriptor.to_owned(),
197 actuator_type,
198 step_count,
199 index: 0,
200 }
201 }
202}
203
204fn range_sequence_serialize<S>(
205 range_vec: &Vec<RangeInclusive<i32>>,
206 serializer: S,
207) -> Result<S::Ok, S::Error>
208where
209 S: Serializer,
210{
211 let mut seq = serializer.serialize_seq(Some(range_vec.len()))?;
212 for range in range_vec {
213 seq.serialize_element(&vec![*range.start(), *range.end()])?;
214 }
215 seq.end()
216}
217
218#[derive(Clone, Debug, Serialize, Deserialize, Getters, Setters)]
219pub struct SensorDeviceMessageAttributesV3 {
220 #[getset(get = "pub")]
221 #[serde(rename = "FeatureDescriptor")]
222 pub(in crate::message) feature_descriptor: String,
223 #[getset(get = "pub")]
224 #[serde(rename = "SensorType")]
225 pub(in crate::message) sensor_type: InputType,
226 #[getset(get = "pub")]
227 #[serde(rename = "SensorRange", serialize_with = "range_sequence_serialize")]
228 pub(in crate::message) sensor_range: Vec<RangeInclusive<i32>>,
229 // TODO This needs to actually be part of the device info relayed to the client in spec v4.
230 #[getset(get = "pub")]
231 #[serde(skip, default)]
232 pub(in crate::message) index: u32,
233 // Matching device feature for this attribute. Do not serialize or deserialize this, it's not part
234 // of this version of the protocol, only use it for comparison when doing message conversion.
235 #[getset(get = "pub")]
236 #[serde(skip)]
237 pub(in crate::message) feature: DeviceFeature,
238}
239
240// This is an almost exact copy of the conversion we do for ServerDeviceFeature ->
241// ServerDeviceMessageAttributesV3, but there's enough differences in the members that it's just
242// easiest to mostly repeat here.
243impl From<Vec<DeviceFeature>> for ClientDeviceMessageAttributesV3 {
244 fn from(features: Vec<DeviceFeature>) -> Self {
245 let scalar_attrs: Vec<ClientGenericDeviceMessageAttributesV3> = features
246 .iter()
247 .flat_map(|feature| {
248 let mut actuator_vec = vec![];
249 if let Some(output_map) = feature.output() {
250 let mut create_actuator =
251 |actuator_type, actuator: &DeviceFeatureOutputValueProperties| {
252 let attrs = ClientGenericDeviceMessageAttributesV3 {
253 feature_descriptor: feature.description().to_owned(),
254 actuator_type,
255 step_count: actuator.step_count(),
256 index: 0,
257 };
258 actuator_vec.push(attrs)
259 };
260 if let Some(x) = output_map.constrict().as_ref() {
261 create_actuator(OutputType::Constrict, x)
262 }
263 if let Some(x) = output_map.temperature().as_ref() {
264 create_actuator(OutputType::Temperature, x)
265 }
266 if let Some(x) = output_map.led().as_ref() {
267 create_actuator(OutputType::Led, x)
268 }
269 if let Some(x) = output_map.oscillate().as_ref() {
270 create_actuator(OutputType::Oscillate, x)
271 }
272 if let Some(x) = output_map.position().as_ref() {
273 create_actuator(OutputType::Position, x)
274 }
275 if let Some(x) = output_map.rotate().as_ref() {
276 create_actuator(OutputType::Rotate, x)
277 }
278 if let Some(x) = output_map.spray().as_ref() {
279 create_actuator(OutputType::Spray, x)
280 }
281 if let Some(x) = output_map.vibrate().as_ref() {
282 create_actuator(OutputType::Vibrate, x)
283 }
284 }
285 actuator_vec
286 })
287 .collect();
288
289 // We have to calculate rotation attributes seperately, since they're a combination of
290 // feature type and message in >= v4.
291 let rotate_attrs: Vec<ClientGenericDeviceMessageAttributesV3> = features
292 .iter()
293 .flat_map(|feature| {
294 let mut actuator_vec = vec![];
295 if let Some(output_map) = feature.output()
296 && let Some(actuator) = output_map.rotate()
297 && *actuator.value().start() < 0
298 {
299 let actuator_type = OutputType::Rotate;
300 let attrs = ClientGenericDeviceMessageAttributesV3 {
301 feature_descriptor: feature.description().to_owned(),
302 actuator_type,
303 step_count: actuator.step_count(),
304 index: 0,
305 };
306 actuator_vec.push(attrs)
307 }
308 actuator_vec
309 })
310 .collect();
311
312 let linear_attrs: Vec<ClientGenericDeviceMessageAttributesV3> = features
313 .iter()
314 .flat_map(|feature| {
315 let mut actuator_vec = vec![];
316 if let Some(output_map) = feature.output()
317 && let Some(actuator) = output_map.position_with_duration()
318 {
319 let actuator_type = OutputType::Position;
320 let attrs = ClientGenericDeviceMessageAttributesV3 {
321 feature_descriptor: feature.description().to_owned(),
322 actuator_type,
323 step_count: actuator.step_count(),
324 index: 0,
325 };
326 actuator_vec.push(attrs)
327 }
328 actuator_vec
329 })
330 .collect();
331
332 let sensor_filter = {
333 let attrs: Vec<SensorDeviceMessageAttributesV3> = features
334 .iter()
335 .map(|feature| {
336 let mut sensor_vec = vec![];
337 if let Some(sensor_map) = feature.input() {
338 // Only convert Battery backwards. Other sensors weren't really built for v3 and we
339 // never recommended using them or implemented much for them.
340 if let Some(battery) = sensor_map.battery()
341 && battery.input_commands().contains(&InputCommandType::Read)
342 {
343 sensor_vec.push(SensorDeviceMessageAttributesV3 {
344 feature_descriptor: feature.description().to_owned(),
345 sensor_type: InputType::Battery,
346 sensor_range: battery.value_range().clone(),
347 feature: feature.clone(),
348 index: 0,
349 });
350 }
351 }
352 sensor_vec
353 })
354 .flatten()
355 .collect();
356 if !attrs.is_empty() { Some(attrs) } else { None }
357 };
358
359 Self {
360 scalar_cmd: if scalar_attrs.is_empty() {
361 None
362 } else {
363 Some(scalar_attrs)
364 },
365 rotate_cmd: if rotate_attrs.is_empty() {
366 None
367 } else {
368 Some(rotate_attrs)
369 },
370 linear_cmd: if linear_attrs.is_empty() {
371 None
372 } else {
373 Some(linear_attrs)
374 },
375 sensor_read_cmd: sensor_filter,
376 sensor_subscribe_cmd: None,
377 ..Default::default()
378 }
379 }
380}