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
8//! Buttplug Message Spec Conversion
9//!
10//! This module contains code to convert any message from an older spec version up to the current
11//! message spec, and then convert any response from the current message spec back down the sending
12//! spec. This is handled within the server, as the server is the only portion of Buttplug that
13//! needs to handle up/downgrading (the client should never have to care and should only ever talk
14//! one version of the spec, preferably the latest). Having this done within the server also allows
15//! us to access required state for converting between messages that requires knowledge of ephemeral
16//! device structures (i.e. converting from v4 device features to <= v3 message attributes for
17//! messages like DeviceAdded).
18
19use buttplug_core::{
20 errors::{ButtplugError, ButtplugMessageError},
21 message::{
22 ButtplugDeviceMessage,
23 ButtplugMessage,
24 ButtplugMessageSpecVersion,
25 ButtplugServerMessageV4,
26 DeviceListV4,
27 DeviceMessageInfoV4,
28 DeviceRemovedV0,
29 InputTypeData,
30 },
31};
32
33use dashmap::DashSet;
34
35use crate::message::{DeviceAddedV0, DeviceAddedV1, DeviceAddedV2, DeviceAddedV3};
36
37use super::message::{
38 BatteryLevelReadingV2,
39 ButtplugClientMessageV2,
40 ButtplugClientMessageV3,
41 ButtplugClientMessageVariant,
42 ButtplugServerMessageV0,
43 ButtplugServerMessageV1,
44 ButtplugServerMessageV2,
45 ButtplugServerMessageV3,
46 ButtplugServerMessageVariant,
47 SensorReadingV3,
48};
49
50pub struct ButtplugServerDeviceEventMessageConverter {
51 device_indexes: DashSet<u32>,
52}
53
54impl ButtplugServerDeviceEventMessageConverter {
55 pub fn new(indexes: Vec<u32>) -> Self {
56 let device_indexes = DashSet::new();
57 indexes.iter().for_each(|x| {
58 device_indexes.insert(*x);
59 });
60 Self { device_indexes }
61 }
62
63 // Due to the way we generate device events, we expect every new DeviceList to only have one
64 // change currently.
65 pub fn convert_device_list(
66 &self,
67 version: &ButtplugMessageSpecVersion,
68 list: &DeviceListV4,
69 ) -> ButtplugServerMessageVariant {
70 let new_indexes: Vec<u32> = list.devices().iter().map(|x| *x.0).collect();
71 if new_indexes.len() > self.device_indexes.len() {
72 // Device Added
73 let connected_devices: Vec<&DeviceMessageInfoV4> = list
74 .devices()
75 .values()
76 .filter(|x| !self.device_indexes.contains(&x.device_index()))
77 .collect();
78 self
79 .device_indexes
80 .insert(connected_devices[0].device_index());
81 if *version == ButtplugMessageSpecVersion::Version4 {
82 return ButtplugServerMessageVariant::V4(list.clone().into());
83 }
84 let da3 = DeviceAddedV3::from(connected_devices[0].clone());
85 if *version == ButtplugMessageSpecVersion::Version3 {
86 return ButtplugServerMessageVariant::V3(da3.into());
87 }
88 let da2 = DeviceAddedV2::from(da3);
89 if *version == ButtplugMessageSpecVersion::Version2 {
90 return ButtplugServerMessageVariant::V2(da2.into());
91 }
92 let da1 = DeviceAddedV1::from(da2);
93 if *version == ButtplugMessageSpecVersion::Version1 {
94 return ButtplugServerMessageVariant::V1(da1.into());
95 }
96 let da0 = DeviceAddedV0::from(da1);
97 ButtplugServerMessageVariant::V0(ButtplugServerMessageV0::DeviceAdded(da0))
98 } else {
99 // Device Removed
100 let disconnected_indexes: Vec<u32> = self
101 .device_indexes
102 .iter()
103 .filter(|x| !new_indexes.contains(x))
104 .map(|x| *x)
105 .collect();
106 self.device_indexes.remove(&disconnected_indexes[0]);
107 match version {
108 ButtplugMessageSpecVersion::Version0 => ButtplugServerMessageVariant::V0(
109 ButtplugServerMessageV0::DeviceRemoved(DeviceRemovedV0::new(disconnected_indexes[0])),
110 ),
111 ButtplugMessageSpecVersion::Version1 => {
112 ButtplugServerMessageVariant::V1(DeviceRemovedV0::new(disconnected_indexes[0]).into())
113 }
114 ButtplugMessageSpecVersion::Version2 => {
115 ButtplugServerMessageVariant::V2(DeviceRemovedV0::new(disconnected_indexes[0]).into())
116 }
117 ButtplugMessageSpecVersion::Version3 => {
118 ButtplugServerMessageVariant::V3(DeviceRemovedV0::new(disconnected_indexes[0]).into())
119 }
120 ButtplugMessageSpecVersion::Version4 => {
121 ButtplugServerMessageVariant::V4(list.clone().into())
122 }
123 }
124 }
125 // There is no == here because the only way DeviceList would be returned is via a
126 // RequestDeviceList call. Events will only ever be additions or deletions.
127 }
128}
129
130pub struct ButtplugServerMessageConverter {
131 original_message: Option<ButtplugClientMessageVariant>,
132}
133
134impl ButtplugServerMessageConverter {
135 pub fn new(msg: Option<ButtplugClientMessageVariant>) -> Self {
136 Self {
137 original_message: msg,
138 }
139 }
140
141 //
142 // Outgoing Conversion
143 //
144
145 pub fn convert_outgoing(
146 &self,
147 msg: &ButtplugServerMessageV4,
148 version: &ButtplugMessageSpecVersion,
149 ) -> Result<ButtplugServerMessageVariant, ButtplugError> {
150 let mut outgoing_msg = match version {
151 ButtplugMessageSpecVersion::Version0 => {
152 ButtplugServerMessageVariant::V0(self.convert_servermessagev4_to_servermessagev0(msg)?)
153 }
154 ButtplugMessageSpecVersion::Version1 => {
155 ButtplugServerMessageVariant::V1(self.convert_servermessagev4_to_servermessagev1(msg)?)
156 }
157 ButtplugMessageSpecVersion::Version2 => {
158 ButtplugServerMessageVariant::V2(self.convert_servermessagev4_to_servermessagev2(msg)?)
159 }
160 ButtplugMessageSpecVersion::Version3 => {
161 ButtplugServerMessageVariant::V3(self.convert_servermessagev4_to_servermessagev3(msg)?)
162 }
163 ButtplugMessageSpecVersion::Version4 => ButtplugServerMessageVariant::V4(msg.clone()),
164 };
165 // Always make sure the ID is set after conversion
166 outgoing_msg.set_id(msg.id());
167 Ok(outgoing_msg)
168 }
169
170 fn convert_servermessagev4_to_servermessagev3(
171 &self,
172 msg: &ButtplugServerMessageV4,
173 ) -> Result<ButtplugServerMessageV3, ButtplugError> {
174 match msg {
175 ButtplugServerMessageV4::InputReading(m) => {
176 let original_msg = self.original_message.as_ref().unwrap();
177 if let ButtplugClientMessageVariant::V3(ButtplugClientMessageV3::SensorReadCmd(msg)) =
178 &original_msg
179 {
180 // We only ever implemented battery in v3, so only accept that.
181 if let InputTypeData::Battery(value) = m.data() {
182 let msg_out = SensorReadingV3::new(
183 msg.device_index(),
184 *msg.sensor_index(),
185 *msg.sensor_type(),
186 vec![value.data() as i32],
187 );
188 Ok(msg_out.into())
189 } else {
190 Err(ButtplugMessageError::UnexpectedMessageType("SensorReading".to_owned()).into())
191 }
192 } else {
193 Err(ButtplugMessageError::UnexpectedMessageType("SensorReading".to_owned()).into())
194 }
195 }
196 _ => Ok(msg.clone().try_into()?),
197 }
198 }
199
200 fn convert_servermessagev4_to_servermessagev2(
201 &self,
202 msg: &ButtplugServerMessageV4,
203 ) -> Result<ButtplugServerMessageV2, ButtplugError> {
204 let msg_v3 = self.convert_servermessagev4_to_servermessagev3(msg)?;
205 match msg_v3 {
206 ButtplugServerMessageV3::SensorReading(m) => {
207 let original_msg = self.original_message.as_ref().unwrap();
208 // Sensor Reading didn't exist in v2, we only had Battery or RSSI. Therefore we need to
209 // context of the original message to make sure this conversion happens correctly.
210 if let ButtplugClientMessageVariant::V2(ButtplugClientMessageV2::BatteryLevelCmd(msg)) =
211 &original_msg
212 {
213 Ok(BatteryLevelReadingV2::new(msg.device_index(), m.data()[0] as f64 / 100f64).into())
214 } else {
215 Err(ButtplugMessageError::UnexpectedMessageType("SensorReading".to_owned()).into())
216 }
217 }
218 _ => Ok(msg_v3.into()),
219 }
220 }
221
222 fn convert_servermessagev4_to_servermessagev1(
223 &self,
224 msg: &ButtplugServerMessageV4,
225 ) -> Result<ButtplugServerMessageV1, ButtplugError> {
226 Ok(self.convert_servermessagev4_to_servermessagev2(msg)?.into())
227 }
228
229 fn convert_servermessagev4_to_servermessagev0(
230 &self,
231 msg: &ButtplugServerMessageV4,
232 ) -> Result<ButtplugServerMessageV0, ButtplugError> {
233 Ok(self.convert_servermessagev4_to_servermessagev1(msg)?.into())
234 }
235
236 // Outgoing Conversion Utility Methods
237}