Trò chuyện với tay điều khiển Stadia bằng WebHID

Tay điều khiển Stadia có đèn flash hoạt động như một tay điều khiển trò chơi tiêu chuẩn, nghĩa là một số nút trên tay điều khiển có thể không dùng được qua Gamepad API. Giờ đây, với WebHID, bạn có thể truy cập vào các nút bị thiếu.

Từ khi Stadia ngừng hoạt động, nhiều người lo ngại rằng tay điều khiển đó sẽ trở thành một phần cứng vô dụng và bị vứt bỏ. Thật may là nhóm Stadia đã quyết định mở tay điều khiển Stadia bằng cách cung cấp một chương trình cơ sở tuỳ chỉnh để bạn có thể cài đặt ROM trên tay điều khiển bằng cách chuyển đến trang chế độ Bluetooth của Stadia. Việc này giúp tay điều khiển Stadia xuất hiện dưới dạng một tay điều khiển trò chơi tiêu chuẩn để bạn có thể kết nối qua cáp USB hoặc Bluetooth không dây. Rất tự hào được giới thiệu tại sự kiện Project Fugu API Showcase, trang Stadia Bluetooth sử dụng WebHIDWebUSB, nhưng đây không phải là chủ đề của bài viết này. Trong bài đăng này, tôi muốn giải thích cách bạn có thể nói chuyện với tay điều khiển Stadia qua WebHID.

Tay điều khiển Stadia làm tay điều khiển trò chơi thông thường

Sau khi cài đặt ROM, tay điều khiển sẽ xuất hiện dưới dạng một tay điều khiển trò chơi tiêu chuẩn trong hệ điều hành. Hãy xem ảnh chụp màn hình sau đây để biết cách sắp xếp trục và nút phổ biến trên tay điều khiển trò chơi tiêu chuẩn. Như đã xác định trong thông số kỹ thuật Gamepad API, tay điều khiển trò chơi tiêu chuẩn có các nút từ 0 đến 16, do đó tổng cộng có 17 (d-pad được tính là 4 nút). Nếu dùng thử tay điều khiển Stadia trong bản minh hoạ của trình kiểm tra tay điều khiển trò chơi, bạn sẽ nhận thấy rằng tay điều khiển này hoạt động như một bùa mê.

Sơ đồ về một tay điều khiển trò chơi tiêu chuẩn với nhiều trục và nút được gắn nhãn.

Tuy nhiên, nếu bạn tính các nút trên tay điều khiển Stadia thì sẽ có 19 nút. Nếu bạn thử lần lượt từng nút một trong trình kiểm thử tay điều khiển trò chơi một cách có hệ thống, bạn sẽ nhận ra rằng nút Assistant (Trợ lý) và nút Capture (Ghi) không hoạt động. Ngay cả khi thuộc tính buttons của tay điều khiển trò chơi như xác định trong thông số kỹ thuật của Tay điều khiển trò chơi là dạng mở, vì tay điều khiển Stadia xuất hiện dưới dạng một tay điều khiển trò chơi tiêu chuẩn, nên chỉ các nút từ 0 đến 16 được liên kết. Bạn vẫn có thể sử dụng các nút khác, nhưng hầu hết trò chơi sẽ không sử dụng được các nút này.

WebHID là giải pháp

Nhờ API WebHID, bạn có thể trao đổi với các nút 17 và 18 bị thiếu. Nếu thực sự muốn, bạn thậm chí có thể nhận dữ liệu về tất cả các nút và trục khác đã có sẵn thông qua Gamepad API. Bước đầu tiên là tìm hiểu cách tay điều khiển Stadia tự báo cáo cho hệ điều hành. Một cách để thực hiện việc này là mở Bảng điều khiển công cụ của Chrome cho nhà phát triển trên một trang ngẫu nhiên bất kỳ và yêu cầu một danh sách thiết bị chưa được lọc từ API WebHID. Sau đó, bạn có thể chọn tay điều khiển Stadia theo cách thủ công để kiểm tra thêm. Lấy danh sách thiết bị chưa lọc bằng cách truyền một mảng tuỳ chọn filters trống.

const [device] = await navigator.hid.requestDevice({filters: []});

Trong bộ chọn, mục áp chót trông giống như tay điều khiển Stadia.

Bộ chọn thiết bị API WebHID cho thấy một số thiết bị không liên quan và tay điều khiển Stadia ở vị trí áp chót.

Sau khi chọn thiết bị "Stadia Controller rev. A", hãy ghi lại đối tượng HIDDevice thu được vào Console. Thao tác này cho thấy productId của tay điều khiển Stadia (37888, có giá trị là 0x9400 theo hệ thập lục phân) và vendorId (6353, là 0x18d1 theo hệ thập lục phân). Nếu bạn tra cứu vendorID trong bảng mã nhà cung cấp USB chính thức, bạn sẽ thấy 6353 liên kết với những gì bạn mong đợi: Google Inc..

Bảng điều khiển Công cụ của Chrome cho nhà phát triển cho thấy kết quả của việc ghi nhật ký đối tượng HIDDevice.

Một cách khác cho quy trình được mô tả ở trên là chuyển đến chrome://device-log/ trong thanh URL, nhấn nút Xoá, kết nối với tay điều khiển Stadia rồi nhấn vào Làm mới. Thao tác này sẽ cung cấp cho bạn cùng một thông tin.

Giao diện gỡ lỗi chrome://device-log hiển thị thông tin về tay điều khiển Stadia đã cắm điện.

Một cách khác là sử dụng công cụ HID Explorer để giúp bạn khám phá nhiều thông tin chi tiết hơn nữa về các thiết bị HID được kết nối với máy tính của bạn.

Sử dụng hai mã nhận dạng này (vendorIdproductId) để tinh chỉnh nội dung hiển thị trong bộ chọn bằng cách lọc chính xác thiết bị WebHID phù hợp.

const [stadiaController] = await navigator.hid.requestDevice({filters: [{
  vendorId: 6353,
  productId: 37888,
}]});

Giờ đây, tất cả thiết bị không liên quan đã biến mất tiếng ồn và chỉ còn tay điều khiển Stadia xuất hiện.

Bộ chọn thiết bị API WebHID chỉ hiển thị tay điều khiển Stadia.

Tiếp theo, hãy mở HIDDevice bằng cách gọi phương thức open().

await stadiaController.open();

Ghi lại HIDDevice và cờ opened được đặt thành true.

Bảng điều khiển Công cụ của Chrome cho nhà phát triển cho thấy kết quả ghi nhật ký đối tượng HIDDevice sau khi mở đối tượng đó.

Khi thiết bị đang mở, hãy theo dõi các sự kiện inputreport sắp tới bằng cách đính kèm trình nghe sự kiện.

stadiaController.addEventListener('inputreport', (e) => {
  console.log(e);
});

Khi bạn nhấn và thả nút Assistant (Trợ lý) trên bộ điều khiển, 2 sự kiện sẽ được ghi lại vào Console. Bạn có thể coi đó là sự kiện "Trợ lý dừng hoạt động" và "Trợ lý nhấn nút". Ngoài timeStamp, 2 sự kiện này khi mới nhìn vào sẽ không thể phân biệt được.

Bảng điều khiển Công cụ của Chrome cho nhà phát triển cho thấy các đối tượng HIDInputReportEvent đang được ghi lại.

Thuộc tính reportId của giao diện HIDInputReportEvent sẽ trả về tiền tố nhận dạng 1 byte cho báo cáo này hoặc 0 nếu giao diện HID không sử dụng mã báo cáo. Trong trường hợp này, giá trị là 3. Khoá bí mật nằm trong thuộc tính data, được biểu thị dưới dạng DataView có kích thước 10. DataView cung cấp giao diện cấp thấp để đọc và ghi nhiều loại số trong tệp nhị phân ArrayBuffer. Bạn có thể hiểu rõ hơn nội dung biểu diễn này bằng cách tạo Uint8Array từ ArrayBuffer, để có thể xem từng số nguyên 8 bit chưa ký.

const data = new Uint8Array(event.data.buffer);

Sau đó, khi bạn ghi lại dữ liệu sự kiện báo cáo nhập, mọi thứ bắt đầu có ý nghĩa hơn và sự kiện "Assistant (Trợ lý) ngừng hoạt động" và "Assistant (Trợ lý) bật lên" bắt đầu trở nên dễ hiểu. Số nguyên đầu tiên (8 trong cả hai sự kiện) có vẻ liên quan đến việc nhấn nút, còn số nguyên thứ hai (20) dường như có liên quan đến việc người dùng có nhấn nút Trợ lý hay không.

Bảng điều khiển Công cụ của Chrome cho nhà phát triển cho thấy các đối tượng Uint8Array đang được ghi lại cho mỗi HIDInputReportEvent.

Nhấn nút Capture thay vì nút Assistant (Trợ lý). Bạn sẽ thấy số nguyên thứ hai bật/tắt từ 1 khi nhấn nút này thành 0 khi nhả ra. Điều này cho phép các em viết một "trình điều khiển" rất đơn giản cho phép các em sử dụng 2 nút còn thiếu.

stadia.addEventListener('inputreport', (event) => {
  if (!e.reportId === 3) {
    return;
  }
  const data = new Uint8Array(event.data.buffer);
  if (data[0] === 8) {
    if (data[1] === 1) {
      hidButtons[1].classList.add('highlight');
    } else if (data[1] === 2) {
      hidButtons[0].classList.add('highlight');
    } else if (data[1] === 3) {
      hidButtons[0].classList.add('highlight');
      hidButtons[1].classList.add('highlight');
    } else {
      hidButtons[0].classList.remove('highlight');
      hidButtons[1].classList.remove('highlight');
    }
  }
});

Khi áp dụng phương pháp kỹ thuật đảo ngược như thế này, bạn có thể tìm ra cách nói chuyện với tay điều khiển Stadia qua WebHID bằng cách nhấn từng nút và từng trục. Sau khi bạn thành thạo, phần còn lại gần như là công việc ánh xạ số nguyên cơ học.

Một điều còn thiếu giờ đây là trải nghiệm kết nối mượt mà mà API Gamepad cung cấp cho bạn. Tuy vì lý do bảo mật, bạn luôn phải kiểm tra phần công cụ chọn ban đầu một lần để thao tác với một thiết bị WebHID như tay điều khiển Stadia. Tuy nhiên, nếu sau này có kết nối, bạn có thể kết nối lại với các thiết bị đã biết. Hãy thực hiện việc đó bằng cách gọi phương thức getDevices().

let stadiaController;
const [device] = await navigator.hid.getDevices();
if (device && device.vendorId === 6353 && device.productId === 37888) {
  stadiaController = device;
}

Bản minh hoạ

Bạn có thể thấy tay điều khiển của Stadia được kiểm soát chung bằng Gamepad API và API WebHID trong một bản minh hoạ mà tôi đã xây dựng. Hãy đảm bảo xem mã nguồn, được xây dựng dựa trên các đoạn trích từ bài viết này. Để đơn giản, tôi chỉ hiển thị các nút A, B, XY (được điều khiển bằng Gamepad API) cũng như các nút Assistant (Trợ lý) và Capture (Ghi) (được điều khiển bằng API WebHID). Bên dưới hình ảnh tay điều khiển, bạn có thể thấy dữ liệu WebHID thô để nắm được tất cả các nút và trục trên tay điều khiển.

Ứng dụng minh hoạ tại https://stadia-controller-webhid-gamepad.glitch.me/ cho thấy các nút A, B, X và Y do API Gamepad kiểm soát, còn nút Trợ lý và nút Capture do API WebHID kiểm soát.

Kết luận

Nhờ chương trình cơ sở mới, tay điều khiển Stadia hiện có thể dùng làm tay điều khiển trò chơi tiêu chuẩn với 17 nút. Trong hầu hết trường hợp, là quá đủ để điều khiển các trò chơi phổ biến trên web. Nếu vì bất kỳ lý do gì, nếu bạn cần dữ liệu từ tất cả 19 nút trên bộ điều khiển, thì WebHID cho phép bạn truy cập vào các báo cáo đầu vào cấp thấp mà bạn có thể giải mã bằng cách thiết kế đảo ngược từng báo cáo một. Nếu bạn tình cờ viết một trình điều khiển WebHID hoàn chỉnh sau khi đọc bài viết này, hãy chắc chắn liên hệ với tôi và tôi sẽ sẵn lòng liên kết dự án của bạn tại đây. Chúc bạn sử dụng WebHIDing vui vẻ!

Xác nhận

Bài viết này đã được François Beaufort xem xét.