mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 08:45:38 +02:00
Fixed multi-controllers, optimize and improve code in relay and nestri-server
This commit is contained in:
@@ -32,7 +32,6 @@ interface GamepadState {
|
||||
|
||||
export class Controller {
|
||||
protected wrtc: WebRTCStream;
|
||||
protected slotMap: Map<number, number> = new Map(); // local slot to server slot
|
||||
protected connected: boolean = false;
|
||||
protected gamepad: Gamepad | null = null;
|
||||
protected lastState: GamepadState = {
|
||||
@@ -50,6 +49,14 @@ export class Controller {
|
||||
protected stickDeadzone: number = 2048; // 2048 / 32768 = ~0.06 (6% of stick range)
|
||||
|
||||
private updateInterval = 10.0; // 100 updates per second
|
||||
private isIdle: boolean = true;
|
||||
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 _dcHandler: ((data: ArrayBuffer) => void) | null = null;
|
||||
|
||||
constructor({ webrtc, e }: Props) {
|
||||
@@ -79,9 +86,8 @@ export class Controller {
|
||||
const attachMsg = messageWrapper.payload.value;
|
||||
// Gamepad connected succesfully
|
||||
this.gamepad = e.gamepad;
|
||||
this.slotMap.set(e.gamepad.index, attachMsg.slot);
|
||||
console.log(
|
||||
`Gamepad connected: ${e.gamepad.id} assigned to slot ${attachMsg.slot} on server, local slot ${e.gamepad.index}`,
|
||||
`Gamepad connected: ${e.gamepad.id}, local slot ${e.gamepad.index}, msg: ${attachMsg.sessionSlot}`,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -93,6 +99,7 @@ export class Controller {
|
||||
const attachMsg = createMessage(
|
||||
create(ProtoControllerAttachSchema, {
|
||||
id: this.vendor_id_to_controller(vendorId, productId),
|
||||
sessionSlot: e.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
}),
|
||||
"controllerInput",
|
||||
@@ -102,6 +109,10 @@ export class Controller {
|
||||
this.run();
|
||||
}
|
||||
|
||||
public getSlot(): number {
|
||||
return this.gamepad.index;
|
||||
}
|
||||
|
||||
// Maps vendor id and product id to supported controller type
|
||||
// Currently supported: Sony (ps4, ps5), Microsoft (xbox360, xboxone), Nintendo (switchpro)
|
||||
// Default fallback to xbox360
|
||||
@@ -154,6 +165,13 @@ export class Controller {
|
||||
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();
|
||||
}
|
||||
|
||||
if (this.gamepad) {
|
||||
if (gamepads[this.gamepad.index]) {
|
||||
this.gamepad = gamepads[this.gamepad!.index];
|
||||
@@ -164,7 +182,7 @@ export class Controller {
|
||||
// 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)) {
|
||||
if (button.pressed !== this.lastState.buttonState.get(index) || this.forceFullStateSend) {
|
||||
const linuxCode = this.controllerButtonToVirtualKeyCode(index);
|
||||
if (linuxCode === undefined) {
|
||||
// Skip unmapped button index
|
||||
@@ -174,13 +192,15 @@ export class Controller {
|
||||
|
||||
const buttonMessage = createMessage(
|
||||
create(ProtoControllerButtonSchema, {
|
||||
slot: this.getServerSlot(),
|
||||
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);
|
||||
}
|
||||
@@ -198,16 +218,18 @@ export class Controller {
|
||||
),
|
||||
);
|
||||
// If state differs, send
|
||||
if (leftTrigger !== this.lastState.leftTrigger) {
|
||||
if (leftTrigger !== this.lastState.leftTrigger || this.forceFullStateSend) {
|
||||
const triggerMessage = createMessage(
|
||||
create(ProtoControllerTriggerSchema, {
|
||||
slot: this.getServerSlot(),
|
||||
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(
|
||||
@@ -220,16 +242,18 @@ export class Controller {
|
||||
),
|
||||
);
|
||||
// If state differs, send
|
||||
if (rightTrigger !== this.lastState.rightTrigger) {
|
||||
if (rightTrigger !== this.lastState.rightTrigger || this.forceFullStateSend) {
|
||||
const triggerMessage = createMessage(
|
||||
create(ProtoControllerTriggerSchema, {
|
||||
slot: this.getServerSlot(),
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -238,32 +262,36 @@ export class Controller {
|
||||
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) {
|
||||
if (dpadX !== this.lastState.dpadX || this.forceFullStateSend) {
|
||||
const dpadMessage = createMessage(
|
||||
create(ProtoControllerAxisSchema, {
|
||||
slot: this.getServerSlot(),
|
||||
sessionSlot: this.gamepad.index,
|
||||
sessionId: this.wrtc.getSessionID(),
|
||||
axis: 0, // 0 = dpadX, 1 = dpadY
|
||||
value: dpadX,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
this.lastState.dpadX = dpadX;
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, dpadMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.dpadX = dpadX;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (dpadY !== this.lastState.dpadY || this.forceFullStateSend) {
|
||||
const dpadMessage = createMessage(
|
||||
create(ProtoControllerAxisSchema, {
|
||||
slot: this.getServerSlot(),
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -292,10 +320,12 @@ export class Controller {
|
||||
// if moves inside deadzone, zero it if not inside deadzone last time
|
||||
if (
|
||||
sendLeftX !== this.lastState.leftX ||
|
||||
sendLeftY !== this.lastState.leftY
|
||||
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,
|
||||
@@ -303,6 +333,7 @@ export class Controller {
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, stickMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.leftX = sendLeftX;
|
||||
this.lastState.leftY = sendLeftY;
|
||||
}
|
||||
@@ -328,10 +359,12 @@ export class Controller {
|
||||
Math.abs(rightY) > this.stickDeadzone ? Math.round(rightY) : 0;
|
||||
if (
|
||||
sendRightX !== this.lastState.rightX ||
|
||||
sendRightY !== this.lastState.rightY
|
||||
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,
|
||||
@@ -339,11 +372,14 @@ export class Controller {
|
||||
"controllerInput",
|
||||
);
|
||||
this.wrtc.sendBinary(toBinary(ProtoMessageSchema, stickMessage));
|
||||
this.inputDetected = true;
|
||||
this.lastState.rightX = sendRightX;
|
||||
this.lastState.rightY = sendRightY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.forceFullStateSend = false;
|
||||
}
|
||||
|
||||
private loopInterval: any = null;
|
||||
@@ -352,10 +388,34 @@ export class Controller {
|
||||
if (this.connected) this.stop();
|
||||
|
||||
this.connected = true;
|
||||
// Poll gamepads in setInterval loop
|
||||
this.isIdle = true;
|
||||
this.lastInputTime = Date.now();
|
||||
|
||||
this.loopInterval = setInterval(() => {
|
||||
if (this.connected) this.pollGamepad();
|
||||
}, this.updateInterval);
|
||||
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);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
@@ -366,21 +426,6 @@ export class Controller {
|
||||
this.connected = false;
|
||||
}
|
||||
|
||||
public getLocalSlot(): number {
|
||||
if (this.gamepad) {
|
||||
return this.gamepad.index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public getServerSlot(): number {
|
||||
if (this.gamepad) {
|
||||
const slot = this.slotMap.get(this.gamepad.index);
|
||||
if (slot !== undefined) return slot;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.stop();
|
||||
// Remove callback
|
||||
@@ -391,7 +436,7 @@ export class Controller {
|
||||
// Gamepad disconnected
|
||||
const detachMsg = createMessage(
|
||||
create(ProtoControllerDetachSchema, {
|
||||
slot: this.getServerSlot(),
|
||||
sessionSlot: this.gamepad.index,
|
||||
}),
|
||||
"controllerInput",
|
||||
);
|
||||
@@ -407,7 +452,9 @@ export class Controller {
|
||||
if (!this.connected) return;
|
||||
|
||||
// Check if aimed at this controller slot
|
||||
if (rumbleMsg.slot !== this.getServerSlot()) return;
|
||||
if (rumbleMsg.sessionId !== this.wrtc.getSessionID() &&
|
||||
rumbleMsg.sessionSlot !== this.gamepad.index)
|
||||
return;
|
||||
|
||||
// Trigger actual rumble
|
||||
// Need to remap from 0-65535 to 0.0-1.0 ranges
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v2.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.0 with parameter "target=ts"
|
||||
// @generated from file latency_tracker.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v2.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.0 with parameter "target=ts"
|
||||
// @generated from file messages.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v2.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v2.10.0 with parameter "target=ts"
|
||||
// @generated from file types.proto (package proto, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf";
|
||||
* Describes the file types.proto.
|
||||
*/
|
||||
export const file_types: GenFile = /*@__PURE__*/
|
||||
fileDesc("Cgt0eXBlcy5wcm90bxIFcHJvdG8iJgoOUHJvdG9Nb3VzZU1vdmUSCQoBeBgBIAEoBRIJCgF5GAIgASgFIikKEVByb3RvTW91c2VNb3ZlQWJzEgkKAXgYASABKAUSCQoBeRgCIAEoBSInCg9Qcm90b01vdXNlV2hlZWwSCQoBeBgBIAEoBRIJCgF5GAIgASgFIiAKEVByb3RvTW91c2VLZXlEb3duEgsKA2tleRgBIAEoBSIeCg9Qcm90b01vdXNlS2V5VXASCwoDa2V5GAEgASgFIhsKDFByb3RvS2V5RG93bhILCgNrZXkYASABKAUiGQoKUHJvdG9LZXlVcBILCgNrZXkYASABKAUiRQoVUHJvdG9Db250cm9sbGVyQXR0YWNoEgoKAmlkGAEgASgJEgwKBHNsb3QYAiABKAUSEgoKc2Vzc2lvbl9pZBgDIAEoCSIlChVQcm90b0NvbnRyb2xsZXJEZXRhY2gSDAoEc2xvdBgBIAEoBSJGChVQcm90b0NvbnRyb2xsZXJCdXR0b24SDAoEc2xvdBgBIAEoBRIOCgZidXR0b24YAiABKAUSDwoHcHJlc3NlZBgDIAEoCCJGChZQcm90b0NvbnRyb2xsZXJUcmlnZ2VyEgwKBHNsb3QYASABKAUSDwoHdHJpZ2dlchgCIAEoBRINCgV2YWx1ZRgDIAEoBSJJChRQcm90b0NvbnRyb2xsZXJTdGljaxIMCgRzbG90GAEgASgFEg0KBXN0aWNrGAIgASgFEgkKAXgYAyABKAUSCQoBeRgEIAEoBSJAChNQcm90b0NvbnRyb2xsZXJBeGlzEgwKBHNsb3QYASABKAUSDAoEYXhpcxgCIAEoBRINCgV2YWx1ZRgDIAEoBSJmChVQcm90b0NvbnRyb2xsZXJSdW1ibGUSDAoEc2xvdBgBIAEoBRIVCg1sb3dfZnJlcXVlbmN5GAIgASgFEhYKDmhpZ2hfZnJlcXVlbmN5GAMgASgFEhAKCGR1cmF0aW9uGAQgASgFIqoBChNSVENJY2VDYW5kaWRhdGVJbml0EhEKCWNhbmRpZGF0ZRgBIAEoCRIaCg1zZHBNTGluZUluZGV4GAIgASgNSACIAQESEwoGc2RwTWlkGAMgASgJSAGIAQESHQoQdXNlcm5hbWVGcmFnbWVudBgEIAEoCUgCiAEBQhAKDl9zZHBNTGluZUluZGV4QgkKB19zZHBNaWRCEwoRX3VzZXJuYW1lRnJhZ21lbnQiNgoZUlRDU2Vzc2lvbkRlc2NyaXB0aW9uSW5pdBILCgNzZHAYASABKAkSDAoEdHlwZRgCIAEoCSI5CghQcm90b0lDRRItCgljYW5kaWRhdGUYASABKAsyGi5wcm90by5SVENJY2VDYW5kaWRhdGVJbml0IjkKCFByb3RvU0RQEi0KA3NkcBgBIAEoCzIgLnByb3RvLlJUQ1Nlc3Npb25EZXNjcmlwdGlvbkluaXQiGAoIUHJvdG9SYXcSDAoEZGF0YRgBIAEoCSJFChxQcm90b0NsaWVudFJlcXVlc3RSb29tU3RyZWFtEhEKCXJvb21fbmFtZRgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJIkcKF1Byb3RvQ2xpZW50RGlzY29ubmVjdGVkEhIKCnNlc3Npb25faWQYASABKAkSGAoQY29udHJvbGxlcl9zbG90cxgCIAMoBSIqChVQcm90b1NlcnZlclB1c2hTdHJlYW0SEQoJcm9vbV9uYW1lGAEgASgJQhZaFHJlbGF5L2ludGVybmFsL3Byb3RvYgZwcm90bzM");
|
||||
fileDesc("Cgt0eXBlcy5wcm90bxIFcHJvdG8iJgoOUHJvdG9Nb3VzZU1vdmUSCQoBeBgBIAEoBRIJCgF5GAIgASgFIikKEVByb3RvTW91c2VNb3ZlQWJzEgkKAXgYASABKAUSCQoBeRgCIAEoBSInCg9Qcm90b01vdXNlV2hlZWwSCQoBeBgBIAEoBRIJCgF5GAIgASgFIiAKEVByb3RvTW91c2VLZXlEb3duEgsKA2tleRgBIAEoBSIeCg9Qcm90b01vdXNlS2V5VXASCwoDa2V5GAEgASgFIhsKDFByb3RvS2V5RG93bhILCgNrZXkYASABKAUiGQoKUHJvdG9LZXlVcBILCgNrZXkYASABKAUiTQoVUHJvdG9Db250cm9sbGVyQXR0YWNoEgoKAmlkGAEgASgJEhQKDHNlc3Npb25fc2xvdBgCIAEoBRISCgpzZXNzaW9uX2lkGAMgASgJIkEKFVByb3RvQ29udHJvbGxlckRldGFjaBIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCSJiChVQcm90b0NvbnRyb2xsZXJCdXR0b24SFAoMc2Vzc2lvbl9zbG90GAEgASgFEhIKCnNlc3Npb25faWQYAiABKAkSDgoGYnV0dG9uGAMgASgFEg8KB3ByZXNzZWQYBCABKAgiYgoWUHJvdG9Db250cm9sbGVyVHJpZ2dlchIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCRIPCgd0cmlnZ2VyGAMgASgFEg0KBXZhbHVlGAQgASgFImUKFFByb3RvQ29udHJvbGxlclN0aWNrEhQKDHNlc3Npb25fc2xvdBgBIAEoBRISCgpzZXNzaW9uX2lkGAIgASgJEg0KBXN0aWNrGAMgASgFEgkKAXgYBCABKAUSCQoBeRgFIAEoBSJcChNQcm90b0NvbnRyb2xsZXJBeGlzEhQKDHNlc3Npb25fc2xvdBgBIAEoBRISCgpzZXNzaW9uX2lkGAIgASgJEgwKBGF4aXMYAyABKAUSDQoFdmFsdWUYBCABKAUiggEKFVByb3RvQ29udHJvbGxlclJ1bWJsZRIUCgxzZXNzaW9uX3Nsb3QYASABKAUSEgoKc2Vzc2lvbl9pZBgCIAEoCRIVCg1sb3dfZnJlcXVlbmN5GAMgASgFEhYKDmhpZ2hfZnJlcXVlbmN5GAQgASgFEhAKCGR1cmF0aW9uGAUgASgFIqoBChNSVENJY2VDYW5kaWRhdGVJbml0EhEKCWNhbmRpZGF0ZRgBIAEoCRIaCg1zZHBNTGluZUluZGV4GAIgASgNSACIAQESEwoGc2RwTWlkGAMgASgJSAGIAQESHQoQdXNlcm5hbWVGcmFnbWVudBgEIAEoCUgCiAEBQhAKDl9zZHBNTGluZUluZGV4QgkKB19zZHBNaWRCEwoRX3VzZXJuYW1lRnJhZ21lbnQiNgoZUlRDU2Vzc2lvbkRlc2NyaXB0aW9uSW5pdBILCgNzZHAYASABKAkSDAoEdHlwZRgCIAEoCSI5CghQcm90b0lDRRItCgljYW5kaWRhdGUYASABKAsyGi5wcm90by5SVENJY2VDYW5kaWRhdGVJbml0IjkKCFByb3RvU0RQEi0KA3NkcBgBIAEoCzIgLnByb3RvLlJUQ1Nlc3Npb25EZXNjcmlwdGlvbkluaXQiGAoIUHJvdG9SYXcSDAoEZGF0YRgBIAEoCSJFChxQcm90b0NsaWVudFJlcXVlc3RSb29tU3RyZWFtEhEKCXJvb21fbmFtZRgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJIkcKF1Byb3RvQ2xpZW50RGlzY29ubmVjdGVkEhIKCnNlc3Npb25faWQYASABKAkSGAoQY29udHJvbGxlcl9zbG90cxgCIAMoBSIqChVQcm90b1NlcnZlclB1c2hTdHJlYW0SEQoJcm9vbV9uYW1lGAEgASgJQhZaFHJlbGF5L2ludGVybmFsL3Byb3RvYgZwcm90bzM");
|
||||
|
||||
/**
|
||||
* MouseMove message
|
||||
@@ -174,14 +174,14 @@ export type ProtoControllerAttach = Message<"proto.ProtoControllerAttach"> & {
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 2;
|
||||
* @generated from field: int32 session_slot = 2;
|
||||
*/
|
||||
slot: number;
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client attaching the controller
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 3;
|
||||
*/
|
||||
@@ -202,11 +202,18 @@ export const ProtoControllerAttachSchema: GenMessage<ProtoControllerAttach> = /*
|
||||
*/
|
||||
export type ProtoControllerDetach = Message<"proto.ProtoControllerDetach"> & {
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 1;
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
slot: number;
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -223,23 +230,30 @@ export const ProtoControllerDetachSchema: GenMessage<ProtoControllerDetach> = /*
|
||||
*/
|
||||
export type ProtoControllerButton = Message<"proto.ProtoControllerButton"> & {
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 1;
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
slot: number;
|
||||
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 = 2;
|
||||
* @generated from field: int32 button = 3;
|
||||
*/
|
||||
button: number;
|
||||
|
||||
/**
|
||||
* true if pressed, false if released
|
||||
*
|
||||
* @generated from field: bool pressed = 3;
|
||||
* @generated from field: bool pressed = 4;
|
||||
*/
|
||||
pressed: boolean;
|
||||
};
|
||||
@@ -258,23 +272,30 @@ export const ProtoControllerButtonSchema: GenMessage<ProtoControllerButton> = /*
|
||||
*/
|
||||
export type ProtoControllerTrigger = Message<"proto.ProtoControllerTrigger"> & {
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 1;
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
slot: number;
|
||||
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 = 2;
|
||||
* @generated from field: int32 trigger = 3;
|
||||
*/
|
||||
trigger: number;
|
||||
|
||||
/**
|
||||
* trigger value (-32768 to 32767)
|
||||
*
|
||||
* @generated from field: int32 value = 3;
|
||||
* @generated from field: int32 value = 4;
|
||||
*/
|
||||
value: number;
|
||||
};
|
||||
@@ -293,30 +314,37 @@ export const ProtoControllerTriggerSchema: GenMessage<ProtoControllerTrigger> =
|
||||
*/
|
||||
export type ProtoControllerStick = Message<"proto.ProtoControllerStick"> & {
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 1;
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
slot: number;
|
||||
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 = 2;
|
||||
* @generated from field: int32 stick = 3;
|
||||
*/
|
||||
stick: number;
|
||||
|
||||
/**
|
||||
* X axis value (-32768 to 32767)
|
||||
*
|
||||
* @generated from field: int32 x = 3;
|
||||
* @generated from field: int32 x = 4;
|
||||
*/
|
||||
x: number;
|
||||
|
||||
/**
|
||||
* Y axis value (-32768 to 32767)
|
||||
*
|
||||
* @generated from field: int32 y = 4;
|
||||
* @generated from field: int32 y = 5;
|
||||
*/
|
||||
y: number;
|
||||
};
|
||||
@@ -335,23 +363,30 @@ export const ProtoControllerStickSchema: GenMessage<ProtoControllerStick> = /*@_
|
||||
*/
|
||||
export type ProtoControllerAxis = Message<"proto.ProtoControllerAxis"> & {
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 1;
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
slot: number;
|
||||
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 = 2;
|
||||
* @generated from field: int32 axis = 3;
|
||||
*/
|
||||
axis: number;
|
||||
|
||||
/**
|
||||
* axis value (-1 to 1)
|
||||
*
|
||||
* @generated from field: int32 value = 3;
|
||||
* @generated from field: int32 value = 4;
|
||||
*/
|
||||
value: number;
|
||||
};
|
||||
@@ -370,30 +405,37 @@ export const ProtoControllerAxisSchema: GenMessage<ProtoControllerAxis> = /*@__P
|
||||
*/
|
||||
export type ProtoControllerRumble = Message<"proto.ProtoControllerRumble"> & {
|
||||
/**
|
||||
* Slot number (0-3)
|
||||
* Session specific slot number (0-3)
|
||||
*
|
||||
* @generated from field: int32 slot = 1;
|
||||
* @generated from field: int32 session_slot = 1;
|
||||
*/
|
||||
slot: number;
|
||||
sessionSlot: number;
|
||||
|
||||
/**
|
||||
* Session ID of the client
|
||||
*
|
||||
* @generated from field: string session_id = 2;
|
||||
*/
|
||||
sessionId: string;
|
||||
|
||||
/**
|
||||
* Low frequency rumble (0-65535)
|
||||
*
|
||||
* @generated from field: int32 low_frequency = 2;
|
||||
* @generated from field: int32 low_frequency = 3;
|
||||
*/
|
||||
lowFrequency: number;
|
||||
|
||||
/**
|
||||
* High frequency rumble (0-65535)
|
||||
*
|
||||
* @generated from field: int32 high_frequency = 3;
|
||||
* @generated from field: int32 high_frequency = 4;
|
||||
*/
|
||||
highFrequency: number;
|
||||
|
||||
/**
|
||||
* Duration in milliseconds
|
||||
*
|
||||
* @generated from field: int32 duration = 4;
|
||||
* @generated from field: int32 duration = 5;
|
||||
*/
|
||||
duration: number;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ import { P2PMessageStream } from "./streamwrapper";
|
||||
const NESTRI_PROTOCOL_STREAM_REQUEST = "/nestri-relay/stream-request/1.0.0";
|
||||
|
||||
export class WebRTCStream {
|
||||
private _sessionId: string | null = null;
|
||||
private _p2p: Libp2p | undefined = undefined;
|
||||
private _p2pConn: Connection | undefined = undefined;
|
||||
private _msgStream: P2PMessageStream | undefined = undefined;
|
||||
@@ -128,9 +129,9 @@ export class WebRTCStream {
|
||||
});
|
||||
|
||||
this._msgStream.on("session-assigned", (data: ProtoClientRequestRoomStream) => {
|
||||
const sessionId = data.sessionId;
|
||||
localStorage.setItem("nestri-session-id", sessionId);
|
||||
console.log("Session ID assigned:", sessionId, "for room:", data.roomName);
|
||||
this._sessionId = data.sessionId;
|
||||
localStorage.setItem("nestri-session-id", this._sessionId);
|
||||
console.log("Session ID assigned:", this._sessionId, "for room:", data.roomName);
|
||||
});
|
||||
|
||||
this._msgStream.on("offer", async (data: ProtoSDP) => {
|
||||
@@ -162,7 +163,7 @@ export class WebRTCStream {
|
||||
this._onConnected?.(null);
|
||||
});
|
||||
|
||||
const clientId = localStorage.getItem("nestri-session-id");
|
||||
const clientId = this.getSessionID();
|
||||
if (clientId) {
|
||||
console.debug("Using existing session ID:", clientId);
|
||||
}
|
||||
@@ -180,8 +181,10 @@ export class WebRTCStream {
|
||||
}
|
||||
}
|
||||
|
||||
public getSessionID(): string {
|
||||
return localStorage.getItem("nestri-session-id") || "";
|
||||
public getSessionID(): string | null {
|
||||
if (this._sessionId === null)
|
||||
this._sessionId = localStorage.getItem("nestri-session-id");
|
||||
return this._sessionId;
|
||||
}
|
||||
|
||||
// Forces opus to stereo in Chromium browsers, because of course
|
||||
@@ -298,7 +301,7 @@ export class WebRTCStream {
|
||||
// @ts-ignore
|
||||
receiver.jitterBufferTarget = receiver.jitterBufferDelayHint = receiver.playoutDelayHint = 0;
|
||||
}
|
||||
}, 15);
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user