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

@@ -0,0 +1,111 @@
import { ButtplugDeviceError } from "../core/Exceptions";
import { OutputType } from "../core/Messages";
class PercentOrSteps {
private _percent: number | undefined;
private _steps: number | undefined;
public get percent() {
return this._percent;
}
public get steps() {
return this._steps;
}
public static createSteps(s: number): PercentOrSteps {
let v = new PercentOrSteps;
v._steps = s;
return v;
}
public static createPercent(p: number): PercentOrSteps {
if (p < 0 || p > 1.0) {
throw new ButtplugDeviceError(`Percent value ${p} is not in the range 0.0 <= x <= 1.0`);
}
let v = new PercentOrSteps;
v._percent = p;
return v;
}
}
export class DeviceOutputCommand {
public constructor(
private _outputType: OutputType,
private _value: PercentOrSteps,
private _duration?: number,
)
{}
public get outputType() {
return this._outputType;
}
public get value() {
return this._value;
}
public get duration() {
return this._duration;
}
}
export class DeviceOutputValueConstructor {
public constructor(
private _outputType: OutputType)
{}
public steps(steps: number): DeviceOutputCommand {
return new DeviceOutputCommand(this._outputType, PercentOrSteps.createSteps(steps), undefined);
}
public percent(percent: number): DeviceOutputCommand {
return new DeviceOutputCommand(this._outputType, PercentOrSteps.createPercent(percent), undefined);
}
}
export class DeviceOutputPositionWithDurationConstructor {
public steps(steps: number, duration: number): DeviceOutputCommand {
return new DeviceOutputCommand(OutputType.Position, PercentOrSteps.createSteps(steps), duration);
}
public percent(percent: number, duration: number): DeviceOutputCommand {
return new DeviceOutputCommand(OutputType.HwPositionWithDuration, PercentOrSteps.createPercent(percent), duration);
}
}
export class DeviceOutput {
private constructor() {}
public static get Vibrate() {
return new DeviceOutputValueConstructor(OutputType.Vibrate);
}
public static get Rotate() {
return new DeviceOutputValueConstructor(OutputType.Rotate);
}
public static get Oscillate() {
return new DeviceOutputValueConstructor(OutputType.Oscillate);
}
public static get Constrict() {
return new DeviceOutputValueConstructor(OutputType.Constrict);
}
public static get Inflate() {
return new DeviceOutputValueConstructor(OutputType.Inflate);
}
public static get Temperature() {
return new DeviceOutputValueConstructor(OutputType.Temperature);
}
public static get Led() {
return new DeviceOutputValueConstructor(OutputType.Led);
}
public static get Spray() {
return new DeviceOutputValueConstructor(OutputType.Spray);
}
public static get Position() {
return new DeviceOutputValueConstructor(OutputType.Position);
}
public static get PositionWithDuration() {
return new DeviceOutputPositionWithDurationConstructor();
}
}