mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 08:45:38 +02:00
More multi-controller fixes, better controller polling logic, clean up dead relay code
This commit is contained in:
@@ -3,10 +3,8 @@ import { WebRTCStream } from "./webrtc-stream";
|
||||
import {
|
||||
ProtoControllerAttachSchema,
|
||||
ProtoControllerDetachSchema,
|
||||
ProtoControllerButtonSchema,
|
||||
ProtoControllerTriggerSchema,
|
||||
ProtoControllerAxisSchema,
|
||||
ProtoControllerStickSchema,
|
||||
ProtoControllerStateBatchSchema,
|
||||
ProtoControllerStateBatch,
|
||||
ProtoControllerRumble,
|
||||
} from "./proto/types_pb";
|
||||
import { create, toBinary, fromBinary } from "@bufbuild/protobuf";
|
||||
@@ -19,6 +17,7 @@ interface Props {
|
||||
}
|
||||
|
||||
interface GamepadState {
|
||||
previousButtonState: Map<number, boolean>;
|
||||
buttonState: Map<number, boolean>;
|
||||
leftTrigger: number;
|
||||
rightTrigger: number;
|
||||
@@ -30,11 +29,17 @@ interface GamepadState {
|
||||
dpadY: number;
|
||||
}
|
||||
|
||||
enum PollState {
|
||||
IDLE,
|
||||
RUNNING,
|
||||
}
|
||||
|
||||
export class Controller {
|
||||
protected wrtc: WebRTCStream;
|
||||
protected connected: boolean = false;
|
||||
protected gamepad: Gamepad | null = null;
|
||||
protected lastState: GamepadState = {
|
||||
protected state: GamepadState = {
|
||||
previousButtonState: new Map<number, boolean>(),
|
||||
buttonState: new Map<number, boolean>(),
|
||||
leftTrigger: 0,
|
||||
rightTrigger: 0,
|
||||
@@ -48,22 +53,34 @@ export class Controller {
|
||||
// TODO: As user configurable, set quite low now for decent controllers (not Nintendo ones :P)
|
||||
protected stickDeadzone: number = 2048; // 2048 / 32768 = ~0.06 (6% of stick range)
|
||||
|
||||
private updateInterval = 10.0; // 100 updates per second
|
||||
private isIdle: boolean = true;
|
||||
// Polling configuration
|
||||
private readonly FULL_RATE_MS = 10; // 100 UPS
|
||||
private readonly IDLE_THRESHOLD = 100; // ms before considering idle/hands off controller
|
||||
private readonly FULL_INTERVAL= 250; // ms before sending full state occassionally, to verify inputs are synced
|
||||
|
||||
// Polling state
|
||||
private pollingState: PollState = PollState.IDLE;
|
||||
private lastInputTime: number = Date.now();
|
||||
private idleUpdateInterval: number = 150.0; // ~6-7 updates per second for keep-alive packets
|
||||
private inputDetected: boolean = false;
|
||||
private lastFullStateSend: number = Date.now();
|
||||
private fullStateSendInterval: number = 500.0; // send full state every 0.5 seconds (helps packet loss)
|
||||
private forceFullStateSend: boolean = false;
|
||||
private lastFullTime: number = Date.now();
|
||||
private pollInterval: any = null;
|
||||
|
||||
// Controller batch vars
|
||||
private sequence: number = 0;
|
||||
private readonly CHANGED_BUTTONS_STATE = 1 << 0;
|
||||
private readonly CHANGED_LEFT_STICK_X = 1 << 1;
|
||||
private readonly CHANGED_LEFT_STICK_Y = 1 << 2;
|
||||
private readonly CHANGED_RIGHT_STICK_X = 1 << 3;
|
||||
private readonly CHANGED_RIGHT_STICK_Y = 1 << 4;
|
||||
private readonly CHANGED_LEFT_TRIGGER = 1 << 5;
|
||||
private readonly CHANGED_RIGHT_TRIGGER = 1 << 6;
|
||||
private readonly CHANGED_DPAD_X = 1 << 7;
|
||||
private readonly CHANGED_DPAD_Y = 1 << 8;
|
||||
|
||||
private _dcHandler: ((data: ArrayBuffer) => void) | null = null;
|
||||
|
||||
constructor({ webrtc, e }: Props) {
|
||||
this.wrtc = webrtc;
|
||||
|
||||
this.updateInterval = 1000 / webrtc.currentFrameRate;
|
||||
|
||||
// Get vendor of gamepad from id string (i.e. "... Vendor: 054c Product: 09cc")
|
||||
const vendorMatch = e.gamepad.id.match(/Vendor:\s?([0-9a-fA-F]{4})/);
|
||||
const vendorId = vendorMatch ? vendorMatch[1].toLowerCase() : "unknown";
|
||||
@@ -89,6 +106,7 @@ export class Controller {
|
||||
console.log(
|
||||
`Gamepad connected: ${e.gamepad.id}, local slot ${e.gamepad.index}, msg: ${attachMsg.sessionSlot}`,
|
||||
);
|
||||
this.run();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error decoding datachannel message:", err);
|
||||
@@ -162,266 +180,283 @@ export class Controller {
|
||||
return ((value - fromMin) * (toMax - toMin)) / (fromMax - fromMin) + toMin;
|
||||
}
|
||||
|
||||
private pollGamepad() {
|
||||
// Get updated gamepad state
|
||||
const gamepads = navigator.getGamepads();
|
||||
|
||||
// Periodically force send full state to clear stuck inputs
|
||||
if (Date.now() - this.lastFullStateSend > this.fullStateSendInterval) {
|
||||
this.forceFullStateSend = true;
|
||||
this.lastFullStateSend = Date.now();
|
||||
private restartPolling() {
|
||||
// Clear existing interval
|
||||
if (this.pollInterval) {
|
||||
clearInterval(this.pollInterval);
|
||||
this.pollInterval = null;
|
||||
}
|
||||
|
||||
if (this.gamepad) {
|
||||
if (gamepads[this.gamepad.index]) {
|
||||
this.gamepad = gamepads[this.gamepad!.index];
|
||||
/* Button handling */
|
||||
this.gamepad.buttons.forEach((button, index) => {
|
||||
// Ignore d-pad buttons (12-15) as we handle those as axis
|
||||
if (index >= 12 && index <= 15) return;
|
||||
// ignore trigger buttons (6-7) as we handle those as axis
|
||||
if (index === 6 || index === 7) return;
|
||||
// If state differs, send
|
||||
if (button.pressed !== this.lastState.buttonState.get(index) || this.forceFullStateSend) {
|
||||
const linuxCode = this.controllerButtonToVirtualKeyCode(index);
|
||||
if (linuxCode === undefined) {
|
||||
// Skip unmapped button index
|
||||
this.lastState.buttonState.set(index, button.pressed);
|
||||
return;
|
||||
}
|
||||
// Restart with active polling
|
||||
this.pollingState = PollState.RUNNING;
|
||||
this.lastInputTime = Date.now();
|
||||
|
||||
const buttonMessage = createMessage(
|
||||
create(ProtoControllerButtonSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
button: linuxCode,
|
||||
pressed: button.pressed,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, buttonMessage));
|
||||
this.inputDetected = true;
|
||||
// Store button state
|
||||
this.lastState.buttonState.set(index, button.pressed);
|
||||
}
|
||||
});
|
||||
// Start interval
|
||||
this.pollInterval = setInterval(
|
||||
() => this.pollGamepad(),
|
||||
this.FULL_RATE_MS,
|
||||
);
|
||||
}
|
||||
|
||||
/* Trigger handling */
|
||||
// map trigger value from 0.0 to 1.0 to -32768 to 32767
|
||||
const leftTrigger = Math.round(
|
||||
this.remapFromTo(
|
||||
this.gamepad.buttons[6]?.value ?? 0,
|
||||
0,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
),
|
||||
);
|
||||
// If state differs, send
|
||||
if (leftTrigger !== this.lastState.leftTrigger || this.forceFullStateSend) {
|
||||
const triggerMessage = createMessage(
|
||||
create(ProtoControllerTriggerSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
trigger: 0, // 0 = left, 1 = right
|
||||
value: leftTrigger,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, triggerMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.leftTrigger = leftTrigger;
|
||||
}
|
||||
const rightTrigger = Math.round(
|
||||
this.remapFromTo(
|
||||
this.gamepad.buttons[7]?.value ?? 0,
|
||||
0,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
),
|
||||
);
|
||||
// If state differs, send
|
||||
if (rightTrigger !== this.lastState.rightTrigger || this.forceFullStateSend) {
|
||||
const triggerMessage = createMessage(
|
||||
create(ProtoControllerTriggerSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
trigger: 1, // 0 = left, 1 = right
|
||||
value: rightTrigger,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, triggerMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.rightTrigger = rightTrigger;
|
||||
}
|
||||
private pollGamepad() {
|
||||
if (!this.connected || !this.gamepad) return;
|
||||
|
||||
/* DPad handling */
|
||||
// We send dpad buttons as axis values -1 to 1 for left/up, right/down
|
||||
const dpadLeft = this.gamepad.buttons[14]?.pressed ? 1 : 0;
|
||||
const dpadRight = this.gamepad.buttons[15]?.pressed ? 1 : 0;
|
||||
const dpadX = dpadLeft ? -1 : dpadRight ? 1 : 0;
|
||||
if (dpadX !== this.lastState.dpadX || this.forceFullStateSend) {
|
||||
const dpadMessage = createMessage(
|
||||
create(ProtoControllerAxisSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
axis: 0, // 0 = dpadX, 1 = dpadY
|
||||
value: dpadX,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, dpadMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.dpadX = dpadX;
|
||||
}
|
||||
const gamepads = navigator.getGamepads();
|
||||
if (!gamepads[this.gamepad.index]) return;
|
||||
|
||||
const dpadUp = this.gamepad.buttons[12]?.pressed ? 1 : 0;
|
||||
const dpadDown = this.gamepad.buttons[13]?.pressed ? 1 : 0;
|
||||
const dpadY = dpadUp ? -1 : dpadDown ? 1 : 0;
|
||||
if (dpadY !== this.lastState.dpadY || this.forceFullStateSend) {
|
||||
const dpadMessage = createMessage(
|
||||
create(ProtoControllerAxisSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
axis: 1, // 0 = dpadX, 1 = dpadY
|
||||
value: dpadY,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, dpadMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.dpadY = dpadY;
|
||||
}
|
||||
this.gamepad = gamepads[this.gamepad.index];
|
||||
|
||||
/* Stick handling */
|
||||
// stick values need to be mapped from -1.0 to 1.0 to -32768 to 32767
|
||||
const leftX = this.remapFromTo(
|
||||
this.gamepad.axes[0] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
const leftY = this.remapFromTo(
|
||||
this.gamepad.axes[1] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
// Apply deadzone
|
||||
const sendLeftX =
|
||||
Math.abs(leftX) > this.stickDeadzone ? Math.round(leftX) : 0;
|
||||
const sendLeftY =
|
||||
Math.abs(leftY) > this.stickDeadzone ? Math.round(leftY) : 0;
|
||||
// if outside deadzone, send normally if changed
|
||||
// if moves inside deadzone, zero it if not inside deadzone last time
|
||||
if (
|
||||
sendLeftX !== this.lastState.leftX ||
|
||||
sendLeftY !== this.lastState.leftY || this.forceFullStateSend
|
||||
) {
|
||||
const stickMessage = createMessage(
|
||||
create(ProtoControllerStickSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
stick: 0, // 0 = left, 1 = right
|
||||
x: sendLeftX,
|
||||
y: sendLeftY,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, stickMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.leftX = sendLeftX;
|
||||
this.lastState.leftY = sendLeftY;
|
||||
}
|
||||
// Collect state changes
|
||||
const changedFields = this.collectStateChanges();
|
||||
|
||||
const rightX = this.remapFromTo(
|
||||
this.gamepad.axes[2] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
const rightY = this.remapFromTo(
|
||||
this.gamepad.axes[3] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
// Apply deadzone
|
||||
const sendRightX =
|
||||
Math.abs(rightX) > this.stickDeadzone ? Math.round(rightX) : 0;
|
||||
const sendRightY =
|
||||
Math.abs(rightY) > this.stickDeadzone ? Math.round(rightY) : 0;
|
||||
if (
|
||||
sendRightX !== this.lastState.rightX ||
|
||||
sendRightY !== this.lastState.rightY || this.forceFullStateSend
|
||||
) {
|
||||
const stickMessage = createMessage(
|
||||
create(ProtoControllerStickSchema, {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
stick: 1, // 0 = left, 1 = right
|
||||
x: sendRightX,
|
||||
y: sendRightY,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, stickMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.rightX = sendRightX;
|
||||
this.lastState.rightY = sendRightY;
|
||||
}
|
||||
// Send batched changes update if there's changes
|
||||
if (changedFields > 0) {
|
||||
let send_type = 1;
|
||||
const timeSinceFull = Date.now() - this.lastFullTime;
|
||||
if (timeSinceFull > this.FULL_INTERVAL) {
|
||||
send_type = 0;
|
||||
this.lastFullTime = Date.now();
|
||||
}
|
||||
|
||||
this.sendBatchedState(changedFields, send_type);
|
||||
this.lastInputTime = Date.now();
|
||||
if (this.pollingState !== PollState.RUNNING) {
|
||||
this.pollingState = PollState.RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
this.forceFullStateSend = false;
|
||||
const timeSinceInput = Date.now() - this.lastInputTime;
|
||||
if (timeSinceInput > this.IDLE_THRESHOLD) {
|
||||
// Changing from running to idle..
|
||||
if (this.pollingState === PollState.RUNNING) {
|
||||
// Send full state on idle assumption
|
||||
this.sendBatchedState(0xFF, 0);
|
||||
this.pollingState = PollState.IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
this.state.buttonState.forEach((b, i) =>
|
||||
this.state.previousButtonState.set(i, b),
|
||||
);
|
||||
}
|
||||
|
||||
private loopInterval: any = null;
|
||||
private collectStateChanges(): number {
|
||||
let changedFields = 0;
|
||||
|
||||
// Collect analog values
|
||||
const leftTrigger = Math.round(
|
||||
this.remapFromTo(
|
||||
this.gamepad.buttons[6]?.value ?? 0,
|
||||
0,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
),
|
||||
);
|
||||
const rightTrigger = Math.round(
|
||||
this.remapFromTo(
|
||||
this.gamepad.buttons[7]?.value ?? 0,
|
||||
0,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
),
|
||||
);
|
||||
|
||||
const leftX = this.remapFromTo(
|
||||
this.gamepad.axes[0] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
const leftY = this.remapFromTo(
|
||||
this.gamepad.axes[1] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
const sendLeftX =
|
||||
Math.abs(leftX) > this.stickDeadzone ? Math.round(leftX) : 0;
|
||||
const sendLeftY =
|
||||
Math.abs(leftY) > this.stickDeadzone ? Math.round(leftY) : 0;
|
||||
|
||||
const rightX = this.remapFromTo(
|
||||
this.gamepad.axes[2] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
const rightY = this.remapFromTo(
|
||||
this.gamepad.axes[3] ?? 0,
|
||||
-1,
|
||||
1,
|
||||
-32768,
|
||||
32767,
|
||||
);
|
||||
const sendRightX =
|
||||
Math.abs(rightX) > this.stickDeadzone ? Math.round(rightX) : 0;
|
||||
const sendRightY =
|
||||
Math.abs(rightY) > this.stickDeadzone ? Math.round(rightY) : 0;
|
||||
|
||||
const dpadX =
|
||||
(this.gamepad.buttons[14]?.pressed ? -1 : 0) +
|
||||
(this.gamepad.buttons[15]?.pressed ? 1 : 0);
|
||||
const dpadY =
|
||||
(this.gamepad.buttons[12]?.pressed ? -1 : 0) +
|
||||
(this.gamepad.buttons[13]?.pressed ? 1 : 0);
|
||||
|
||||
// Check what changed
|
||||
for (let i = 0; i < this.gamepad.buttons.length; i++) {
|
||||
if (i >= 6 && i <= 7) continue; // Skip triggers
|
||||
if (i >= 12 && i <= 15) continue; // Skip d-pad
|
||||
if (this.state.buttonState.get(i) !== this.gamepad.buttons[i].pressed) {
|
||||
changedFields |= this.CHANGED_BUTTONS_STATE;
|
||||
}
|
||||
this.state.buttonState.set(i, this.gamepad.buttons[i].pressed);
|
||||
}
|
||||
if (leftTrigger !== this.state.leftTrigger) {
|
||||
changedFields |= this.CHANGED_LEFT_TRIGGER;
|
||||
}
|
||||
this.state.leftTrigger = leftTrigger;
|
||||
if (rightTrigger !== this.state.rightTrigger) {
|
||||
changedFields |= this.CHANGED_RIGHT_TRIGGER;
|
||||
}
|
||||
this.state.rightTrigger = rightTrigger;
|
||||
if (sendLeftX !== this.state.leftX) {
|
||||
changedFields |= this.CHANGED_LEFT_STICK_X;
|
||||
}
|
||||
this.state.leftX = sendLeftX;
|
||||
if (sendLeftY !== this.state.leftY) {
|
||||
changedFields |= this.CHANGED_LEFT_STICK_Y;
|
||||
}
|
||||
this.state.leftY = sendLeftY;
|
||||
if (sendRightX !== this.state.rightX) {
|
||||
changedFields |= this.CHANGED_RIGHT_STICK_X;
|
||||
}
|
||||
this.state.rightX = sendRightX;
|
||||
if (sendRightY !== this.state.rightY) {
|
||||
changedFields |= this.CHANGED_RIGHT_STICK_Y;
|
||||
}
|
||||
this.state.rightY = sendRightY;
|
||||
if (dpadX !== this.state.dpadX) {
|
||||
changedFields |= this.CHANGED_DPAD_X;
|
||||
}
|
||||
this.state.dpadX = dpadX;
|
||||
if (dpadY !== this.state.dpadY) {
|
||||
changedFields |= this.CHANGED_DPAD_Y;
|
||||
}
|
||||
this.state.dpadY = dpadY;
|
||||
|
||||
return changedFields;
|
||||
}
|
||||
|
||||
private sendBatchedState(changedFields: number, updateType: number) {
|
||||
// @ts-ignore
|
||||
let message: ProtoControllerStateBatch = {
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
updateType: updateType,
|
||||
sequence: this.sequence++,
|
||||
};
|
||||
|
||||
// For FULL_STATE, include everything
|
||||
if (updateType === 0) {
|
||||
message.changedFields = 0xFF;
|
||||
|
||||
message.buttonChangedMask = Object.fromEntries(
|
||||
Array.from(this.state.buttonState).map(([key, value]) => {
|
||||
return [this.controllerButtonToVirtualKeyCode(key), value];
|
||||
}),
|
||||
);
|
||||
message.leftStickX = this.state.leftX;
|
||||
message.leftStickY = this.state.leftY;
|
||||
message.rightStickX = this.state.rightX;
|
||||
message.rightStickY = this.state.rightY;
|
||||
message.leftTrigger = this.state.leftTrigger;
|
||||
message.rightTrigger = this.state.rightTrigger;
|
||||
message.dpadX = this.state.dpadX;
|
||||
message.dpadY = this.state.dpadY;
|
||||
}
|
||||
// For DELTA, only include changed fields
|
||||
else {
|
||||
message.changedFields = changedFields;
|
||||
|
||||
if (changedFields & this.CHANGED_BUTTONS_STATE) {
|
||||
const currentStateMap = this.state.buttonState;
|
||||
const previousStateMap = this.state.previousButtonState;
|
||||
const allKeys = new Set([
|
||||
// @ts-ignore
|
||||
...currentStateMap.keys(),
|
||||
// @ts-ignore
|
||||
...previousStateMap.keys(),
|
||||
]);
|
||||
message.buttonChangedMask = Object.fromEntries(
|
||||
Array.from(allKeys)
|
||||
.filter((key) => {
|
||||
const newState = currentStateMap.get(key);
|
||||
const oldState = previousStateMap.get(key);
|
||||
return newState !== oldState;
|
||||
})
|
||||
.map((key) => {
|
||||
const newValue = currentStateMap.get(key) ?? false;
|
||||
|
||||
return [this.controllerButtonToVirtualKeyCode(key), newValue];
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (changedFields & this.CHANGED_LEFT_STICK_X) {
|
||||
message.leftStickX = this.state.leftX;
|
||||
}
|
||||
if (changedFields & this.CHANGED_LEFT_STICK_Y) {
|
||||
message.leftStickY = this.state.leftY;
|
||||
}
|
||||
if (changedFields & this.CHANGED_RIGHT_STICK_X) {
|
||||
message.rightStickX = this.state.rightX;
|
||||
}
|
||||
if (changedFields & this.CHANGED_RIGHT_STICK_Y) {
|
||||
message.rightStickY = this.state.rightY;
|
||||
}
|
||||
if (changedFields & this.CHANGED_LEFT_TRIGGER) {
|
||||
message.leftTrigger = this.state.leftTrigger;
|
||||
}
|
||||
if (changedFields & this.CHANGED_RIGHT_TRIGGER) {
|
||||
message.rightTrigger = this.state.rightTrigger;
|
||||
}
|
||||
if (changedFields & this.CHANGED_DPAD_X) {
|
||||
message.dpadX = this.state.dpadX;
|
||||
}
|
||||
if (changedFields & this.CHANGED_DPAD_Y) {
|
||||
message.dpadY = this.state.dpadY;
|
||||
}
|
||||
}
|
||||
|
||||
// Send message
|
||||
const batchMessage = createMessage(
|
||||
create(
|
||||
ProtoControllerStateBatchSchema,
|
||||
message as ProtoControllerStateBatch,
|
||||
),
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, batchMessage));
|
||||
}
|
||||
|
||||
public run() {
|
||||
if (this.connected) this.stop();
|
||||
|
||||
this.connected = true;
|
||||
this.isIdle = true;
|
||||
this.lastInputTime = Date.now();
|
||||
|
||||
this.loopInterval = setInterval(() => {
|
||||
if (this.connected) {
|
||||
this.inputDetected = false; // Reset before poll
|
||||
this.pollGamepad();
|
||||
|
||||
// Switch polling rate based on input
|
||||
if (this.inputDetected) {
|
||||
this.lastInputTime = Date.now();
|
||||
if (this.isIdle) {
|
||||
this.isIdle = false;
|
||||
clearInterval(this.loopInterval);
|
||||
this.loopInterval = setInterval(() => {
|
||||
if (this.connected) this.pollGamepad();
|
||||
}, this.updateInterval);
|
||||
}
|
||||
} else if (!this.isIdle && Date.now() - this.lastInputTime > 200) {
|
||||
// Switch to idle polling after 200ms of no input
|
||||
this.isIdle = true;
|
||||
clearInterval(this.loopInterval);
|
||||
this.loopInterval = setInterval(() => {
|
||||
if (this.connected) this.pollGamepad();
|
||||
}, this.idleUpdateInterval);
|
||||
}
|
||||
}
|
||||
}, this.isIdle ? this.idleUpdateInterval : this.updateInterval);
|
||||
// Start with active polling
|
||||
this.restartPolling();
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if (this.loopInterval) {
|
||||
clearInterval(this.loopInterval);
|
||||
this.loopInterval = null;
|
||||
if (this.pollInterval) {
|
||||
clearInterval(this.pollInterval);
|
||||
this.pollInterval = null;
|
||||
}
|
||||
this.connected = false;
|
||||
}
|
||||
@@ -443,17 +478,18 @@ export class Controller {
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, detachMsg));
|
||||
}
|
||||
|
||||
private controllerButtonToVirtualKeyCode(code: number) {
|
||||
private controllerButtonToVirtualKeyCode(code: number): number | undefined {
|
||||
return controllerButtonToLinuxEventCode[code] || undefined;
|
||||
}
|
||||
|
||||
private rumbleCallback(rumbleMsg: ProtoControllerRumble) {
|
||||
// If not connected, ignore
|
||||
if (!this.connected) return;
|
||||
if (!this.connected || !this.gamepad) return;
|
||||
|
||||
// Check if aimed at this controller slot
|
||||
if (rumbleMsg.sessionId !== this.wrtc.getSessionID() &&
|
||||
rumbleMsg.sessionSlot !== this.gamepad.index)
|
||||
// Check if this rumble is for us
|
||||
if (
|
||||
rumbleMsg.sessionId !== this.wrtc.getSessionID() &&
|
||||
rumbleMsg.sessionSlot !== this.gamepad.index
|
||||
)
|
||||
return;
|
||||
|
||||
// Trigger actual rumble
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import type { ProtoClientDisconnected, ProtoClientRequestRoomStream, ProtoControllerAttach, ProtoControllerAxis, ProtoControllerButton, ProtoControllerDetach, ProtoControllerRumble, ProtoControllerStick, ProtoControllerTrigger, ProtoICE, ProtoKeyDown, ProtoKeyUp, ProtoMouseKeyDown, ProtoMouseKeyUp, ProtoMouseMove, ProtoMouseMoveAbs, ProtoMouseWheel, ProtoRaw, ProtoSDP, ProtoServerPushStream } from "./types_pb";
|
||||
import type { ProtoClientDisconnected, ProtoClientRequestRoomStream, ProtoControllerAttach, ProtoControllerDetach, ProtoControllerRumble, ProtoControllerStateBatch, ProtoICE, ProtoKeyDown, ProtoKeyUp, ProtoMouseKeyDown, ProtoMouseKeyUp, ProtoMouseMove, ProtoMouseMoveAbs, ProtoMouseWheel, ProtoRaw, ProtoSDP, ProtoServerPushStream } from "./types_pb";
|
||||
import { file_types } from "./types_pb";
|
||||
import type { ProtoLatencyTracker } from "./latency_tracker_pb";
|
||||
import { file_latency_tracker } from "./latency_tracker_pb";
|
||||
@@ -14,7 +14,7 @@ import type { Message } from "@bufbuild/protobuf";
|
||||
* Describes the file messages.proto.
|
||||
*/
|
||||
export const file_messages: GenFile = /*@__PURE__*/
|
||||
fileDesc("Cg5tZXNzYWdlcy5wcm90bxIFcHJvdG8iVQoQUHJvdG9NZXNzYWdlQmFzZRIUCgxwYXlsb2FkX3R5cGUYASABKAkSKwoHbGF0ZW5jeRgCIAEoCzIaLnByb3RvLlByb3RvTGF0ZW5jeVRyYWNrZXIiyQgKDFByb3RvTWVzc2FnZRItCgxtZXNzYWdlX2Jhc2UYASABKAsyFy5wcm90by5Qcm90b01lc3NhZ2VCYXNlEisKCm1vdXNlX21vdmUYAiABKAsyFS5wcm90by5Qcm90b01vdXNlTW92ZUgAEjIKDm1vdXNlX21vdmVfYWJzGAMgASgLMhgucHJvdG8uUHJvdG9Nb3VzZU1vdmVBYnNIABItCgttb3VzZV93aGVlbBgEIAEoCzIWLnByb3RvLlByb3RvTW91c2VXaGVlbEgAEjIKDm1vdXNlX2tleV9kb3duGAUgASgLMhgucHJvdG8uUHJvdG9Nb3VzZUtleURvd25IABIuCgxtb3VzZV9rZXlfdXAYBiABKAsyFi5wcm90by5Qcm90b01vdXNlS2V5VXBIABInCghrZXlfZG93bhgHIAEoCzITLnByb3RvLlByb3RvS2V5RG93bkgAEiMKBmtleV91cBgIIAEoCzIRLnByb3RvLlByb3RvS2V5VXBIABI5ChFjb250cm9sbGVyX2F0dGFjaBgJIAEoCzIcLnByb3RvLlByb3RvQ29udHJvbGxlckF0dGFjaEgAEjkKEWNvbnRyb2xsZXJfZGV0YWNoGAogASgLMhwucHJvdG8uUHJvdG9Db250cm9sbGVyRGV0YWNoSAASOQoRY29udHJvbGxlcl9idXR0b24YCyABKAsyHC5wcm90by5Qcm90b0NvbnRyb2xsZXJCdXR0b25IABI7ChJjb250cm9sbGVyX3RyaWdnZXIYDCABKAsyHS5wcm90by5Qcm90b0NvbnRyb2xsZXJUcmlnZ2VySAASNwoQY29udHJvbGxlcl9zdGljaxgNIAEoCzIbLnByb3RvLlByb3RvQ29udHJvbGxlclN0aWNrSAASNQoPY29udHJvbGxlcl9heGlzGA4gASgLMhoucHJvdG8uUHJvdG9Db250cm9sbGVyQXhpc0gAEjkKEWNvbnRyb2xsZXJfcnVtYmxlGA8gASgLMhwucHJvdG8uUHJvdG9Db250cm9sbGVyUnVtYmxlSAASHgoDaWNlGBQgASgLMg8ucHJvdG8uUHJvdG9JQ0VIABIeCgNzZHAYFSABKAsyDy5wcm90by5Qcm90b1NEUEgAEh4KA3JhdxgWIAEoCzIPLnByb3RvLlByb3RvUmF3SAASSQoaY2xpZW50X3JlcXVlc3Rfcm9vbV9zdHJlYW0YFyABKAsyIy5wcm90by5Qcm90b0NsaWVudFJlcXVlc3RSb29tU3RyZWFtSAASPQoTY2xpZW50X2Rpc2Nvbm5lY3RlZBgYIAEoCzIeLnByb3RvLlByb3RvQ2xpZW50RGlzY29ubmVjdGVkSAASOgoSc2VydmVyX3B1c2hfc3RyZWFtGBkgASgLMhwucHJvdG8uUHJvdG9TZXJ2ZXJQdXNoU3RyZWFtSABCCQoHcGF5bG9hZEIWWhRyZWxheS9pbnRlcm5hbC9wcm90b2IGcHJvdG8z", [file_types, file_latency_tracker]);
|
||||
fileDesc("Cg5tZXNzYWdlcy5wcm90bxIFcHJvdG8iVQoQUHJvdG9NZXNzYWdlQmFzZRIUCgxwYXlsb2FkX3R5cGUYASABKAkSKwoHbGF0ZW5jeRgCIAEoCzIaLnByb3RvLlByb3RvTGF0ZW5jeVRyYWNrZXIipQcKDFByb3RvTWVzc2FnZRItCgxtZXNzYWdlX2Jhc2UYASABKAsyFy5wcm90by5Qcm90b01lc3NhZ2VCYXNlEisKCm1vdXNlX21vdmUYAiABKAsyFS5wcm90by5Qcm90b01vdXNlTW92ZUgAEjIKDm1vdXNlX21vdmVfYWJzGAMgASgLMhgucHJvdG8uUHJvdG9Nb3VzZU1vdmVBYnNIABItCgttb3VzZV93aGVlbBgEIAEoCzIWLnByb3RvLlByb3RvTW91c2VXaGVlbEgAEjIKDm1vdXNlX2tleV9kb3duGAUgASgLMhgucHJvdG8uUHJvdG9Nb3VzZUtleURvd25IABIuCgxtb3VzZV9rZXlfdXAYBiABKAsyFi5wcm90by5Qcm90b01vdXNlS2V5VXBIABInCghrZXlfZG93bhgHIAEoCzITLnByb3RvLlByb3RvS2V5RG93bkgAEiMKBmtleV91cBgIIAEoCzIRLnByb3RvLlByb3RvS2V5VXBIABI5ChFjb250cm9sbGVyX2F0dGFjaBgJIAEoCzIcLnByb3RvLlByb3RvQ29udHJvbGxlckF0dGFjaEgAEjkKEWNvbnRyb2xsZXJfZGV0YWNoGAogASgLMhwucHJvdG8uUHJvdG9Db250cm9sbGVyRGV0YWNoSAASOQoRY29udHJvbGxlcl9ydW1ibGUYCyABKAsyHC5wcm90by5Qcm90b0NvbnRyb2xsZXJSdW1ibGVIABJCChZjb250cm9sbGVyX3N0YXRlX2JhdGNoGAwgASgLMiAucHJvdG8uUHJvdG9Db250cm9sbGVyU3RhdGVCYXRjaEgAEh4KA2ljZRgUIAEoCzIPLnByb3RvLlByb3RvSUNFSAASHgoDc2RwGBUgASgLMg8ucHJvdG8uUHJvdG9TRFBIABIeCgNyYXcYFiABKAsyDy5wcm90by5Qcm90b1Jhd0gAEkkKGmNsaWVudF9yZXF1ZXN0X3Jvb21fc3RyZWFtGBcgASgLMiMucHJvdG8uUHJvdG9DbGllbnRSZXF1ZXN0Um9vbVN0cmVhbUgAEj0KE2NsaWVudF9kaXNjb25uZWN0ZWQYGCABKAsyHi5wcm90by5Qcm90b0NsaWVudERpc2Nvbm5lY3RlZEgAEjoKEnNlcnZlcl9wdXNoX3N0cmVhbRgZIAEoCzIcLnByb3RvLlByb3RvU2VydmVyUHVzaFN0cmVhbUgAQgkKB3BheWxvYWRCFloUcmVsYXkvaW50ZXJuYWwvcHJvdG9iBnByb3RvMw", [file_types, file_latency_tracker]);
|
||||
|
||||
/**
|
||||
* @generated from message proto.ProtoMessageBase
|
||||
@@ -96,6 +96,8 @@ export type ProtoMessage = Message<"proto.ProtoMessage"> & {
|
||||
case: "keyUp";
|
||||
} | {
|
||||
/**
|
||||
* Controller input types
|
||||
*
|
||||
* @generated from field: proto.ProtoControllerAttach controller_attach = 9;
|
||||
*/
|
||||
value: ProtoControllerAttach;
|
||||
@@ -108,34 +110,16 @@ export type ProtoMessage = Message<"proto.ProtoMessage"> & {
|
||||
case: "controllerDetach";
|
||||
} | {
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerButton controller_button = 11;
|
||||
*/
|
||||
value: ProtoControllerButton;
|
||||
case: "controllerButton";
|
||||
} | {
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerTrigger controller_trigger = 12;
|
||||
*/
|
||||
value: ProtoControllerTrigger;
|
||||
case: "controllerTrigger";
|
||||
} | {
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerStick controller_stick = 13;
|
||||
*/
|
||||
value: ProtoControllerStick;
|
||||
case: "controllerStick";
|
||||
} | {
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerAxis controller_axis = 14;
|
||||
*/
|
||||
value: ProtoControllerAxis;
|
||||
case: "controllerAxis";
|
||||
} | {
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerRumble controller_rumble = 15;
|
||||
* @generated from field: proto.ProtoControllerRumble controller_rumble = 11;
|
||||
*/
|
||||
value: ProtoControllerRumble;
|
||||
case: "controllerRumble";
|
||||
} | {
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerStateBatch controller_state_batch = 12;
|
||||
*/
|
||||
value: ProtoControllerStateBatch;
|
||||
case: "controllerStateBatch";
|
||||
} | {
|
||||
/**
|
||||
* Signaling types
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
// @generated from file types.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||
import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import type { Message } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* Describes the file types.proto.
|
||||
*/
|
||||
export const file_types: GenFile = /*@__PURE__*/
|
||||
fileDesc("Cgt0eXBlcy5wcm90bxIFcHJvdG8iJgoOUHJvdG9Nb3VzZU1vdmUSCQoBeBgBIAEoBRIJCgF5GAIgASgFIikKEVByb3RvTW91c2VNb3ZlQWJzEgkKAXgYASABKAUSCQoBeRgCIAEoBSInCg9Qcm90b01vdXNlV2hlZWwSCQoBeBgBIAEoBRIJCgF5GAIgASgFIiAKEVByb3RvTW91c2VLZXlEb3duEgsKA2tleRgBIAEoBSIeCg9Qcm90b01vdXNlS2V5VXASCwoDa2V5GAEgASgFIhsKDFByb3RvS2V5RG93bhILCgNrZXkYASABKAUiGQoKUHJvdG9LZXlVcBILCgNrZXkYASABKAUiTQoVUHJvdG9Db250cm9sbGVyQXR0YWNoEgoKAmlkGAEgASgJEhQKDHNlc3Npb25fc2xvdBgCIAEoBRISCgpzZXNzaW9uX2lkGAMgASgJIkEKFVByb3RvQ29udHJvbGxlckRldGFjaBIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCSJiChVQcm90b0NvbnRyb2xsZXJCdXR0b24SFAoMc2Vzc2lvbl9zbG90GAEgASgFEhIKCnNlc3Npb25faWQYAiABKAkSDgoGYnV0dG9uGAMgASgFEg8KB3ByZXNzZWQYBCABKAgiYgoWUHJvdG9Db250cm9sbGVyVHJpZ2dlchIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCRIPCgd0cmlnZ2VyGAMgASgFEg0KBXZhbHVlGAQgASgFImUKFFByb3RvQ29udHJvbGxlclN0aWNrEhQKDHNlc3Npb25fc2xvdBgBIAEoBRISCgpzZXNzaW9uX2lkGAIgASgJEg0KBXN0aWNrGAMgASgFEgkKAXgYBCABKAUSCQoBeRgFIAEoBSJcChNQcm90b0NvbnRyb2xsZXJBeGlzEhQKDHNlc3Npb25fc2xvdBgBIAEoBRISCgpzZXNzaW9uX2lkGAIgASgJEgwKBGF4aXMYAyABKAUSDQoFdmFsdWUYBCABKAUiggEKFVByb3RvQ29udHJvbGxlclJ1bWJsZRIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCRIVCg1sb3dfZnJlcXVlbmN5GAMgASgFEhYKDmhpZ2hfZnJlcXVlbmN5GAQgASgFEhAKCGR1cmF0aW9uGAUgASgFIqoBChNSVENJY2VDYW5kaWRhdGVJbml0EhEKCWNhbmRpZGF0ZRgBIAEoCRIaCg1zZHBNTGluZUluZGV4GAIgASgNSACIAQESEwoGc2RwTWlkGAMgASgJSAGIAQESHQoQdXNlcm5hbWVGcmFnbWVudBgEIAEoCUgCiAEBQhAKDl9zZHBNTGluZUluZGV4QgkKB19zZHBNaWRCEwoRX3VzZXJuYW1lRnJhZ21lbnQiNgoZUlRDU2Vzc2lvbkRlc2NyaXB0aW9uSW5pdBILCgNzZHAYASABKAkSDAoEdHlwZRgCIAEoCSI5CghQcm90b0lDRRItCgljYW5kaWRhdGUYASABKAsyGi5wcm90by5SVENJY2VDYW5kaWRhdGVJbml0IjkKCFByb3RvU0RQEi0KA3NkcBgBIAEoCzIgLnByb3RvLlJUQ1Nlc3Npb25EZXNjcmlwdGlvbkluaXQiGAoIUHJvdG9SYXcSDAoEZGF0YRgBIAEoCSJFChxQcm90b0NsaWVudFJlcXVlc3RSb29tU3RyZWFtEhEKCXJvb21fbmFtZRgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJIkcKF1Byb3RvQ2xpZW50RGlzY29ubmVjdGVkEhIKCnNlc3Npb25faWQYASABKAkSGAoQY29udHJvbGxlcl9zbG90cxgCIAMoBSIqChVQcm90b1NlcnZlclB1c2hTdHJlYW0SEQoJcm9vbV9uYW1lGAEgASgJQhZaFHJlbGF5L2ludGVybmFsL3Byb3RvYgZwcm90bzM");
|
||||
fileDesc("Cgt0eXBlcy5wcm90bxIFcHJvdG8iJgoOUHJvdG9Nb3VzZU1vdmUSCQoBeBgBIAEoBRIJCgF5GAIgASgFIikKEVByb3RvTW91c2VNb3ZlQWJzEgkKAXgYASABKAUSCQoBeRgCIAEoBSInCg9Qcm90b01vdXNlV2hlZWwSCQoBeBgBIAEoBRIJCgF5GAIgASgFIiAKEVByb3RvTW91c2VLZXlEb3duEgsKA2tleRgBIAEoBSIeCg9Qcm90b01vdXNlS2V5VXASCwoDa2V5GAEgASgFIhsKDFByb3RvS2V5RG93bhILCgNrZXkYASABKAUiGQoKUHJvdG9LZXlVcBILCgNrZXkYASABKAUiTQoVUHJvdG9Db250cm9sbGVyQXR0YWNoEgoKAmlkGAEgASgJEhQKDHNlc3Npb25fc2xvdBgCIAEoBRISCgpzZXNzaW9uX2lkGAMgASgJIkEKFVByb3RvQ29udHJvbGxlckRldGFjaBIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCSKCAQoVUHJvdG9Db250cm9sbGVyUnVtYmxlEhQKDHNlc3Npb25fc2xvdBgBIAEoBRISCgpzZXNzaW9uX2lkGAIgASgJEhUKDWxvd19mcmVxdWVuY3kYAyABKAUSFgoOaGlnaF9mcmVxdWVuY3kYBCABKAUSEAoIZHVyYXRpb24YBSABKAUi0AUKGVByb3RvQ29udHJvbGxlclN0YXRlQmF0Y2gSFAoMc2Vzc2lvbl9zbG90GAEgASgFEhIKCnNlc3Npb25faWQYAiABKAkSQAoLdXBkYXRlX3R5cGUYAyABKA4yKy5wcm90by5Qcm90b0NvbnRyb2xsZXJTdGF0ZUJhdGNoLlVwZGF0ZVR5cGUSEAoIc2VxdWVuY2UYBCABKA0SVAoTYnV0dG9uX2NoYW5nZWRfbWFzaxgFIAMoCzI3LnByb3RvLlByb3RvQ29udHJvbGxlclN0YXRlQmF0Y2guQnV0dG9uQ2hhbmdlZE1hc2tFbnRyeRIZCgxsZWZ0X3N0aWNrX3gYBiABKAVIAIgBARIZCgxsZWZ0X3N0aWNrX3kYByABKAVIAYgBARIaCg1yaWdodF9zdGlja194GAggASgFSAKIAQESGgoNcmlnaHRfc3RpY2tfeRgJIAEoBUgDiAEBEhkKDGxlZnRfdHJpZ2dlchgKIAEoBUgEiAEBEhoKDXJpZ2h0X3RyaWdnZXIYCyABKAVIBYgBARITCgZkcGFkX3gYDCABKAVIBogBARITCgZkcGFkX3kYDSABKAVIB4gBARIbCg5jaGFuZ2VkX2ZpZWxkcxgOIAEoDUgIiAEBGjgKFkJ1dHRvbkNoYW5nZWRNYXNrRW50cnkSCwoDa2V5GAEgASgFEg0KBXZhbHVlGAIgASgIOgI4ASInCgpVcGRhdGVUeXBlEg4KCkZVTExfU1RBVEUQABIJCgVERUxUQRABQg8KDV9sZWZ0X3N0aWNrX3hCDwoNX2xlZnRfc3RpY2tfeUIQCg5fcmlnaHRfc3RpY2tfeEIQCg5fcmlnaHRfc3RpY2tfeUIPCg1fbGVmdF90cmlnZ2VyQhAKDl9yaWdodF90cmlnZ2VyQgkKB19kcGFkX3hCCQoHX2RwYWRfeUIRCg9fY2hhbmdlZF9maWVsZHMiqgEKE1JUQ0ljZUNhbmRpZGF0ZUluaXQSEQoJY2FuZGlkYXRlGAEgASgJEhoKDXNkcE1MaW5lSW5kZXgYAiABKA1IAIgBARITCgZzZHBNaWQYAyABKAlIAYgBARIdChB1c2VybmFtZUZyYWdtZW50GAQgASgJSAKIAQFCEAoOX3NkcE1MaW5lSW5kZXhCCQoHX3NkcE1pZEITChFfdXNlcm5hbWVGcmFnbWVudCI2ChlSVENTZXNzaW9uRGVzY3JpcHRpb25Jbml0EgsKA3NkcBgBIAEoCRIMCgR0eXBlGAIgASgJIjkKCFByb3RvSUNFEi0KCWNhbmRpZGF0ZRgBIAEoCzIaLnByb3RvLlJUQ0ljZUNhbmRpZGF0ZUluaXQiOQoIUHJvdG9TRFASLQoDc2RwGAEgASgLMiAucHJvdG8uUlRDU2Vzc2lvbkRlc2NyaXB0aW9uSW5pdCIYCghQcm90b1JhdxIMCgRkYXRhGAEgASgJIkUKHFByb3RvQ2xpZW50UmVxdWVzdFJvb21TdHJlYW0SEQoJcm9vbV9uYW1lGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkiRwoXUHJvdG9DbGllbnREaXNjb25uZWN0ZWQSEgoKc2Vzc2lvbl9pZBgBIAEoCRIYChBjb250cm9sbGVyX3Nsb3RzGAIgAygFIioKFVByb3RvU2VydmVyUHVzaFN0cmVhbRIRCglyb29tX25hbWUYASABKAlCFloUcmVsYXkvaW50ZXJuYWwvcHJvdG9iBnByb3RvMw");
|
||||
|
||||
/**
|
||||
* MouseMove message
|
||||
@@ -223,181 +223,6 @@ export type ProtoControllerDetach = Message<"proto.ProtoControllerDetach"> & {
|
||||
export const ProtoControllerDetachSchema: GenMessage<ProtoControllerDetach> = /*@__PURE__*/
|
||||
messageDesc(file_types, 8);
|
||||
|
||||
/**
|
||||
* ControllerButton message
|
||||
*
|
||||
* @generated from message proto.ProtoControllerButton
|
||||
*/
|
||||
export type ProtoControllerButton = Message<"proto.ProtoControllerButton"> & {
|
||||
/**
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
|
||||
/**
|
||||
* Button code (linux input event code)
|
||||
*
|
||||
* @generated from field: int32 button = 3;
|
||||
*/
|
||||
button: number;
|
||||
|
||||
/**
|
||||
* true if pressed, false if released
|
||||
*
|
||||
* @generated from field: bool pressed = 4;
|
||||
*/
|
||||
pressed: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.ProtoControllerButton.
|
||||
* Use `create(ProtoControllerButtonSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoControllerButtonSchema: GenMessage<ProtoControllerButton> = /*@__PURE__*/
|
||||
messageDesc(file_types, 9);
|
||||
|
||||
/**
|
||||
* ControllerTriggers message
|
||||
*
|
||||
* @generated from message proto.ProtoControllerTrigger
|
||||
*/
|
||||
export type ProtoControllerTrigger = Message<"proto.ProtoControllerTrigger"> & {
|
||||
/**
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
|
||||
/**
|
||||
* Trigger number (0 for left, 1 for right)
|
||||
*
|
||||
* @generated from field: int32 trigger = 3;
|
||||
*/
|
||||
trigger: number;
|
||||
|
||||
/**
|
||||
* trigger value (-32768 to 32767)
|
||||
*
|
||||
* @generated from field: int32 value = 4;
|
||||
*/
|
||||
value: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.ProtoControllerTrigger.
|
||||
* Use `create(ProtoControllerTriggerSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoControllerTriggerSchema: GenMessage<ProtoControllerTrigger> = /*@__PURE__*/
|
||||
messageDesc(file_types, 10);
|
||||
|
||||
/**
|
||||
* ControllerSticks message
|
||||
*
|
||||
* @generated from message proto.ProtoControllerStick
|
||||
*/
|
||||
export type ProtoControllerStick = Message<"proto.ProtoControllerStick"> & {
|
||||
/**
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
|
||||
/**
|
||||
* Stick number (0 for left, 1 for right)
|
||||
*
|
||||
* @generated from field: int32 stick = 3;
|
||||
*/
|
||||
stick: number;
|
||||
|
||||
/**
|
||||
* X axis value (-32768 to 32767)
|
||||
*
|
||||
* @generated from field: int32 x = 4;
|
||||
*/
|
||||
x: number;
|
||||
|
||||
/**
|
||||
* Y axis value (-32768 to 32767)
|
||||
*
|
||||
* @generated from field: int32 y = 5;
|
||||
*/
|
||||
y: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.ProtoControllerStick.
|
||||
* Use `create(ProtoControllerStickSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoControllerStickSchema: GenMessage<ProtoControllerStick> = /*@__PURE__*/
|
||||
messageDesc(file_types, 11);
|
||||
|
||||
/**
|
||||
* ControllerAxis message
|
||||
*
|
||||
* @generated from message proto.ProtoControllerAxis
|
||||
*/
|
||||
export type ProtoControllerAxis = Message<"proto.ProtoControllerAxis"> & {
|
||||
/**
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
|
||||
/**
|
||||
* Axis number (0 for d-pad horizontal, 1 for d-pad vertical)
|
||||
*
|
||||
* @generated from field: int32 axis = 3;
|
||||
*/
|
||||
axis: number;
|
||||
|
||||
/**
|
||||
* axis value (-1 to 1)
|
||||
*
|
||||
* @generated from field: int32 value = 4;
|
||||
*/
|
||||
value: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.ProtoControllerAxis.
|
||||
* Use `create(ProtoControllerAxisSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoControllerAxisSchema: GenMessage<ProtoControllerAxis> = /*@__PURE__*/
|
||||
messageDesc(file_types, 12);
|
||||
|
||||
/**
|
||||
* ControllerRumble message
|
||||
*
|
||||
@@ -445,7 +270,145 @@ export type ProtoControllerRumble = Message<"proto.ProtoControllerRumble"> & {
|
||||
* Use `create(ProtoControllerRumbleSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoControllerRumbleSchema: GenMessage<ProtoControllerRumble> = /*@__PURE__*/
|
||||
messageDesc(file_types, 13);
|
||||
messageDesc(file_types, 9);
|
||||
|
||||
/**
|
||||
* ControllerStateBatch - single message containing full or partial controller state
|
||||
*
|
||||
* @generated from message proto.ProtoControllerStateBatch
|
||||
*/
|
||||
export type ProtoControllerStateBatch = Message<"proto.ProtoControllerStateBatch"> & {
|
||||
/**
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
|
||||
/**
|
||||
* @generated from field: proto.ProtoControllerStateBatch.UpdateType update_type = 3;
|
||||
*/
|
||||
updateType: ProtoControllerStateBatch_UpdateType;
|
||||
|
||||
/**
|
||||
* Sequence number for packet loss detection
|
||||
*
|
||||
* @generated from field: uint32 sequence = 4;
|
||||
*/
|
||||
sequence: number;
|
||||
|
||||
/**
|
||||
* Button state map (Linux event codes)
|
||||
*
|
||||
* @generated from field: map<int32, bool> button_changed_mask = 5;
|
||||
*/
|
||||
buttonChangedMask: { [key: number]: boolean };
|
||||
|
||||
/**
|
||||
* Analog inputs
|
||||
*
|
||||
* -32768 to 32767
|
||||
*
|
||||
* @generated from field: optional int32 left_stick_x = 6;
|
||||
*/
|
||||
leftStickX?: number;
|
||||
|
||||
/**
|
||||
* -32768 to 32767
|
||||
*
|
||||
* @generated from field: optional int32 left_stick_y = 7;
|
||||
*/
|
||||
leftStickY?: number;
|
||||
|
||||
/**
|
||||
* -32768 to 32767
|
||||
*
|
||||
* @generated from field: optional int32 right_stick_x = 8;
|
||||
*/
|
||||
rightStickX?: number;
|
||||
|
||||
/**
|
||||
* -32768 to 32767
|
||||
*
|
||||
* @generated from field: optional int32 right_stick_y = 9;
|
||||
*/
|
||||
rightStickY?: number;
|
||||
|
||||
/**
|
||||
* -32768 to 32767
|
||||
*
|
||||
* @generated from field: optional int32 left_trigger = 10;
|
||||
*/
|
||||
leftTrigger?: number;
|
||||
|
||||
/**
|
||||
* -32768 to 32767
|
||||
*
|
||||
* @generated from field: optional int32 right_trigger = 11;
|
||||
*/
|
||||
rightTrigger?: number;
|
||||
|
||||
/**
|
||||
* -1, 0, or 1
|
||||
*
|
||||
* @generated from field: optional int32 dpad_x = 12;
|
||||
*/
|
||||
dpadX?: number;
|
||||
|
||||
/**
|
||||
* -1, 0, or 1
|
||||
*
|
||||
* @generated from field: optional int32 dpad_y = 13;
|
||||
*/
|
||||
dpadY?: number;
|
||||
|
||||
/**
|
||||
* Bitmask indicating which fields have changed
|
||||
* Bit 0: button_changed_mask, Bit 1: left_stick_x, Bit 2: left_stick_y, etc.
|
||||
*
|
||||
* @generated from field: optional uint32 changed_fields = 14;
|
||||
*/
|
||||
changedFields?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.ProtoControllerStateBatch.
|
||||
* Use `create(ProtoControllerStateBatchSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoControllerStateBatchSchema: GenMessage<ProtoControllerStateBatch> = /*@__PURE__*/
|
||||
messageDesc(file_types, 10);
|
||||
|
||||
/**
|
||||
* @generated from enum proto.ProtoControllerStateBatch.UpdateType
|
||||
*/
|
||||
export enum ProtoControllerStateBatch_UpdateType {
|
||||
/**
|
||||
* Complete controller state
|
||||
*
|
||||
* @generated from enum value: FULL_STATE = 0;
|
||||
*/
|
||||
FULL_STATE = 0,
|
||||
|
||||
/**
|
||||
* Only changed fields
|
||||
*
|
||||
* @generated from enum value: DELTA = 1;
|
||||
*/
|
||||
DELTA = 1,
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the enum proto.ProtoControllerStateBatch.UpdateType.
|
||||
*/
|
||||
export const ProtoControllerStateBatch_UpdateTypeSchema: GenEnum<ProtoControllerStateBatch_UpdateType> = /*@__PURE__*/
|
||||
enumDesc(file_types, 10, 0);
|
||||
|
||||
/**
|
||||
* @generated from message proto.RTCIceCandidateInit
|
||||
@@ -477,7 +440,7 @@ export type RTCIceCandidateInit = Message<"proto.RTCIceCandidateInit"> & {
|
||||
* Use `create(RTCIceCandidateInitSchema)` to create a new message.
|
||||
*/
|
||||
export const RTCIceCandidateInitSchema: GenMessage<RTCIceCandidateInit> = /*@__PURE__*/
|
||||
messageDesc(file_types, 14);
|
||||
messageDesc(file_types, 11);
|
||||
|
||||
/**
|
||||
* @generated from message proto.RTCSessionDescriptionInit
|
||||
@@ -499,7 +462,7 @@ export type RTCSessionDescriptionInit = Message<"proto.RTCSessionDescriptionInit
|
||||
* Use `create(RTCSessionDescriptionInitSchema)` to create a new message.
|
||||
*/
|
||||
export const RTCSessionDescriptionInitSchema: GenMessage<RTCSessionDescriptionInit> = /*@__PURE__*/
|
||||
messageDesc(file_types, 15);
|
||||
messageDesc(file_types, 12);
|
||||
|
||||
/**
|
||||
* ProtoICE message
|
||||
@@ -518,7 +481,7 @@ export type ProtoICE = Message<"proto.ProtoICE"> & {
|
||||
* Use `create(ProtoICESchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoICESchema: GenMessage<ProtoICE> = /*@__PURE__*/
|
||||
messageDesc(file_types, 16);
|
||||
messageDesc(file_types, 13);
|
||||
|
||||
/**
|
||||
* ProtoSDP message
|
||||
@@ -537,7 +500,7 @@ export type ProtoSDP = Message<"proto.ProtoSDP"> & {
|
||||
* Use `create(ProtoSDPSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoSDPSchema: GenMessage<ProtoSDP> = /*@__PURE__*/
|
||||
messageDesc(file_types, 17);
|
||||
messageDesc(file_types, 14);
|
||||
|
||||
/**
|
||||
* ProtoRaw message
|
||||
@@ -556,7 +519,7 @@ export type ProtoRaw = Message<"proto.ProtoRaw"> & {
|
||||
* Use `create(ProtoRawSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoRawSchema: GenMessage<ProtoRaw> = /*@__PURE__*/
|
||||
messageDesc(file_types, 18);
|
||||
messageDesc(file_types, 15);
|
||||
|
||||
/**
|
||||
* ProtoClientRequestRoomStream message
|
||||
@@ -580,7 +543,7 @@ export type ProtoClientRequestRoomStream = Message<"proto.ProtoClientRequestRoom
|
||||
* Use `create(ProtoClientRequestRoomStreamSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoClientRequestRoomStreamSchema: GenMessage<ProtoClientRequestRoomStream> = /*@__PURE__*/
|
||||
messageDesc(file_types, 19);
|
||||
messageDesc(file_types, 16);
|
||||
|
||||
/**
|
||||
* ProtoClientDisconnected message
|
||||
@@ -604,7 +567,7 @@ export type ProtoClientDisconnected = Message<"proto.ProtoClientDisconnected"> &
|
||||
* Use `create(ProtoClientDisconnectedSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoClientDisconnectedSchema: GenMessage<ProtoClientDisconnected> = /*@__PURE__*/
|
||||
messageDesc(file_types, 20);
|
||||
messageDesc(file_types, 17);
|
||||
|
||||
/**
|
||||
* ProtoServerPushStream message
|
||||
@@ -623,5 +586,5 @@ export type ProtoServerPushStream = Message<"proto.ProtoServerPushStream"> & {
|
||||
* Use `create(ProtoServerPushStreamSchema)` to create a new message.
|
||||
*/
|
||||
export const ProtoServerPushStreamSchema: GenMessage<ProtoServerPushStream> = /*@__PURE__*/
|
||||
messageDesc(file_types, 21);
|
||||
messageDesc(file_types, 18);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user