feat: upgrade buttplug package to protocol v4 and WASM v10
All checks were successful
Build and Push Docker Image to Gitea / build-and-push (push) Successful in 7m30s

Upgrade the buttplug TypeScript client from class-based v3 protocol to
interface-based v4 protocol, and the Rust/WASM server from the monolithic
buttplug 9.0.9 crate to the split buttplug_core/buttplug_server/
buttplug_server_device_config 10.0.0 crates.

TypeScript changes:
- Messages are now plain interfaces with msgId()/setMsgId() helpers
- ActuatorType → OutputType, SensorType → InputType
- ScalarCmd/RotateCmd/LinearCmd → OutputCmd, SensorReadCmd → InputCmd
- Client.ts → ButtplugClient.ts, new DeviceCommand/DeviceFeature files
- Devices getter returns Map instead of array
- Removed class-transformer/reflect-metadata dependencies

Rust/WASM changes:
- Split imports across buttplug_core, buttplug_server, buttplug_server_device_config
- Removed ButtplugServerDowngradeWrapper (use ButtplugServer directly)
- Replaced ButtplugFuture/ButtplugFutureStateShared with tokio::sync::oneshot
- Updated Hardware::new for new 6-arg signature
- Uses git fork (valknarthing/buttplug) to fix missing wasm deps in buttplug_core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 14:46:47 +01:00
parent fed2dd65e5
commit 6ea4ed1933
31 changed files with 1763 additions and 2441 deletions

View File

@@ -6,60 +6,61 @@
* @copyright Copyright (c) Nonpolynomial Labs LLC. All rights reserved.
*/
import * as Messages from "../core/Messages";
import { ButtplugError } from "../core/Exceptions";
import * as Messages from '../core/Messages';
import { ButtplugError } from '../core/Exceptions';
export class ButtplugMessageSorter {
protected _counter = 1;
protected _waitingMsgs: Map<
number,
[(val: Messages.ButtplugMessage) => void, (err: Error) => void]
> = new Map();
protected _counter = 1;
protected _waitingMsgs: Map<
number,
[(val: Messages.ButtplugMessage) => void, (err: Error) => void]
> = new Map();
public constructor(private _useCounter: boolean) {}
public constructor(private _useCounter: boolean) {}
// One of the places we should actually return a promise, as we need to store
// them while waiting for them to return across the line.
// tslint:disable:promise-function-async
public PrepareOutgoingMessage(
msg: Messages.ButtplugMessage,
): Promise<Messages.ButtplugMessage> {
if (this._useCounter) {
msg.Id = this._counter;
// Always increment last, otherwise we might lose sync
this._counter += 1;
}
let res;
let rej;
const msgPromise = new Promise<Messages.ButtplugMessage>(
(resolve, reject) => {
res = resolve;
rej = reject;
},
);
this._waitingMsgs.set(msg.Id, [res, rej]);
return msgPromise;
}
// One of the places we should actually return a promise, as we need to store
// them while waiting for them to return across the line.
// tslint:disable:promise-function-async
public PrepareOutgoingMessage(
msg: Messages.ButtplugMessage
): Promise<Messages.ButtplugMessage> {
if (this._useCounter) {
Messages.setMsgId(msg, this._counter);
// Always increment last, otherwise we might lose sync
this._counter += 1;
}
let res;
let rej;
const msgPromise = new Promise<Messages.ButtplugMessage>(
(resolve, reject) => {
res = resolve;
rej = reject;
}
);
this._waitingMsgs.set(Messages.msgId(msg), [res, rej]);
return msgPromise;
}
public ParseIncomingMessages(
msgs: Messages.ButtplugMessage[],
): Messages.ButtplugMessage[] {
const noMatch: Messages.ButtplugMessage[] = [];
for (const x of msgs) {
if (x.Id !== Messages.SYSTEM_MESSAGE_ID && this._waitingMsgs.has(x.Id)) {
const [res, rej] = this._waitingMsgs.get(x.Id)!;
// If we've gotten back an error, reject the related promise using a
// ButtplugException derived type.
if (x.Type === Messages.Error) {
rej(ButtplugError.FromError(x as Messages.Error));
continue;
}
res(x);
continue;
} else {
noMatch.push(x);
}
}
return noMatch;
}
public ParseIncomingMessages(
msgs: Messages.ButtplugMessage[]
): Messages.ButtplugMessage[] {
const noMatch: Messages.ButtplugMessage[] = [];
for (const x of msgs) {
let id = Messages.msgId(x);
if (id !== Messages.SYSTEM_MESSAGE_ID && this._waitingMsgs.has(id)) {
const [res, rej] = this._waitingMsgs.get(id)!;
// If we've gotten back an error, reject the related promise using a
// ButtplugException derived type.
if (x.Error !== undefined) {
rej(ButtplugError.FromError(x.Error!));
continue;
}
res(x);
continue;
} else {
noMatch.push(x);
}
}
return noMatch;
}
}