플래시된 Stadia 컨트롤러는 표준 게임패드처럼 작동하므로 게임패드 API를 사용하여 모든 버튼에 액세스할 수 있는 것은 아닙니다. 이제 WebHID를 사용하여 누락된 버튼에 액세스할 수 있습니다.
Stadia가 종료된 이후 많은 사람들이 컨트롤러가 매립지에 버려지는 쓸모없는 하드웨어가 될까 봐 우려했습니다. 다행히 Stadia팀은 Stadia 블루투스 모드 페이지로 이동하여 컨트롤러에 플래시할 수 있는 맞춤 펌웨어를 제공하여 Stadia 컨트롤러를 대신 개방하기로 결정했습니다. 이렇게 하면 Stadia 컨트롤러가 USB 케이블을 통해 연결하거나 블루투스를 통해 무선으로 연결할 수 있는 표준 게임패드로 표시됩니다. Project Fugu API Showcase에 소개된 Stadia 블루투스 페이지 자체는 WebHID와 WebUSB를 사용하지만 이 도움말의 주제는 아닙니다. 이 게시물에서는 WebHID를 통해 Stadia 컨트롤러와 대화하는 방법을 설명합니다.
Stadia 컨트롤러를 표준 게임패드로 사용
플래시 후 컨트롤러는 운영체제에 표준 게임패드로 표시됩니다. 표준 게임패드의 일반적인 버튼 및 축 배열은 다음 스크린샷을 참고하세요. 게임패드 API 사양에 정의된 대로 표준 게임패드에는 0~16까지의 버튼이 있으므로 총 17개입니다 (d패드는 4개의 버튼으로 계산됨). 게임패드 테스터 데모에서 Stadia 컨트롤러를 사용해 보면 원활하게 작동하는 것을 확인할 수 있습니다.
하지만 Stadia 컨트롤러의 버튼을 세어 보면 19개입니다. 게임패드 테스터에서 하나씩 체계적으로 시도하면 어시스턴트 및 캡처 버튼이 작동하지 않는다는 것을 알 수 있습니다. 게임패드 사양에 정의된 게임패드 buttons
속성이 개방형이라도 Stadia 컨트롤러는 표준 게임패드로 표시되므로 버튼 0~16만 매핑됩니다. 다른 버튼은 계속 사용할 수 있지만 대부분의 게임에서는 이러한 버튼이 존재하지 않는다고 가정합니다.
WebHID로 문제 해결
WebHID API 덕분에 누락된 버튼 17과 18에 대해 이야기할 수 있습니다. 원하는 경우 Gamepad API를 통해 이미 사용할 수 있는 다른 모든 버튼과 축에 관한 데이터도 가져올 수 있습니다. 첫 번째 단계는 Stadia 컨트롤러가 운영체제에 어떻게 보고되는지 확인하는 것입니다. 한 가지 방법은 임의의 페이지에서 Chrome DevTools 콘솔을 열고 WebHID API에서 필터링되지 않은 기기 목록을 요청하는 것입니다. 그런 다음 추가 검사를 위해 Stadia 컨트롤러를 수동으로 선택합니다. 빈 filters
옵션 배열을 전달하여 필터링되지 않은 기기 목록을 가져옵니다.
const [device] = await navigator.hid.requestDevice({filters: []});
선택기에서 마지막에서 두 번째 항목이 Stadia 컨트롤러처럼 보입니다.
'Stadia Controller rev. A' 기기를 선택한 후 결과 HIDDevice
객체를 콘솔에 로깅합니다. 이렇게 하면 Stadia 컨트롤러의 productId
(37888
, 16진수로는 0x9400
) 및 vendorId
(6353
, 16진수로는 0x18d1
)이 표시됩니다. 공식 USB 공급업체 ID 표에서 vendorID
를 조회하면 6353
가 예상대로 Google Inc.
에 매핑됩니다.
위에서 설명한 흐름의 대안은 URL 표시줄에서 chrome://device-log/
로 이동하고 지우기 버튼을 누르고 Stadia 컨트롤러를 연결한 다음 새로고침을 누르는 것입니다. 이렇게 하면 동일한 정보가 제공됩니다.
컴퓨터에 연결된 HID 기기의 세부정보를 더 자세히 살펴볼 수 있는 HID 탐색기 도구를 사용하는 방법도 있습니다.
이 두 ID(vendorId
및 productId
)를 사용하여 올바른 WebHID 기기를 올바르게 필터링하여 선택기에 표시되는 항목을 개선합니다.
const [stadiaController] = await navigator.hid.requestDevice({filters: [{
vendorId: 6353,
productId: 37888,
}]});
이제 관련 없는 모든 기기의 소음이 사라지고 Stadia 컨트롤러만 표시됩니다.
다음으로 open()
메서드를 호출하여 HIDDevice
를 엽니다.
await stadiaController.open();
HIDDevice
을 다시 로깅하면 opened
플래그가 true
로 설정됩니다.
기기가 열린 상태에서 이벤트 리스너를 연결하여 수신되는 inputreport
이벤트를 수신 대기합니다.
stadiaController.addEventListener('inputreport', (e) => {
console.log(e);
});
컨트롤러에서 어시스턴트 버튼을 눌렀다가 떼면 콘솔에 두 개의 이벤트가 로깅됩니다. '어시스턴트 버튼 다운' 및 '어시스턴트 버튼 업' 이벤트로 생각하면 됩니다. timeStamp
를 제외하면 두 이벤트는 한눈에 구별할 수 없습니다.
HIDInputReportEvent
인터페이스의 reportId
속성은 이 보고서의 1바이트 식별 접두사를 반환하거나 HID 인터페이스가 보고서 ID를 사용하지 않는 경우 0
를 반환합니다. 이 경우 3
입니다. 보안 비밀은 크기가 10인 DataView
으로 표시되는 data
속성에 있습니다. DataView
는 바이너리 ArrayBuffer
에서 여러 숫자 유형을 읽고 쓰는 하위 수준 인터페이스를 제공합니다. 이 표현에서 더 이해하기 쉬운 것을 얻는 방법은 ArrayBuffer
에서 Uint8Array
를 만들어 개별 8비트 부호 없는 정수를 볼 수 있도록 하는 것입니다.
const data = new Uint8Array(event.data.buffer);
그런 다음 입력 보고서 이벤트 데이터를 다시 로깅하면 상황이 더 명확해지고 '어시스턴트 버튼 다운' 및 '어시스턴트 버튼 업' 이벤트를 파악할 수 있게 됩니다. 첫 번째 정수 (두 이벤트 모두 8
)는 버튼 누르기와 관련이 있는 것으로 보이며 두 번째 정수 (2
및 0
)는 어시스턴트 버튼을 눌렀는지 여부와 관련이 있는 것으로 보입니다.
어시스턴트 버튼 대신 캡처 버튼을 누르면 버튼을 눌렀을 때 두 번째 정수가 1
에서 버튼을 뗐을 때 0
로 전환되는 것을 확인할 수 있습니다. 이렇게 하면 누락된 두 버튼을 사용할 수 있는 매우 간단한 '드라이버'를 작성할 수 있습니다.
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');
}
}
});
이와 같은 리버스 엔지니어링 접근 방식을 사용하면 버튼별, 축별로 WebHID를 사용하여 Stadia 컨트롤러와 통신하는 방법을 파악할 수 있습니다. 이 방법을 익히면 나머지는 거의 기계적인 정수 매핑 작업입니다.
이제 게임패드 API가 제공하는 원활한 연결 환경이 누락되었습니다. 보안상의 이유로 Stadia 컨트롤러와 같은 WebHID 기기를 사용하려면 항상 초기 선택기 환경을 한 번 거쳐야 하지만, 향후 연결에서는 알려진 기기에 다시 연결할 수 있습니다. getDevices()
메서드를 호출하면 됩니다.
let stadiaController;
const [device] = await navigator.hid.getDevices();
if (device && device.vendorId === 6353 && device.productId === 37888) {
stadiaController = device;
}
데모
제가 만든 데모에서 Gamepad API와 WebHID API가 공동으로 제어하는 Stadia 컨트롤러를 확인할 수 있습니다. 이 도움말의 스니펫을 기반으로 빌드되는 소스 코드를 확인하세요. 간단하게 하기 위해 게임패드 API로 제어되는 A, B, X, Y 버튼과 WebHID API로 제어되는 어시스턴트, 캡처 버튼만 표시합니다. 컨트롤러 이미지 아래에 원시 WebHID 데이터가 표시되므로 컨트롤러의 모든 버튼과 축을 파악할 수 있습니다.
결론
새 펌웨어 덕분에 이제 Stadia 컨트롤러를 17개의 버튼이 있는 표준 게임패드로 사용할 수 있으며, 대부분의 경우 일반적인 웹 게임을 제어하기에 충분합니다. 어떤 이유로든 컨트롤러의 19개 버튼에서 데이터를 가져와야 하는 경우 WebHID를 사용하면 역엔지니어링을 통해 하나씩 해독할 수 있는 하위 수준 입력 보고서에 액세스할 수 있습니다. 이 도움말을 읽은 후 완전한 WebHID 드라이버를 작성한 경우 저에게 연락해 주세요. 프로젝트를 여기에 링크해 드리겠습니다. WebHID를 즐겁게 사용하세요.
감사의 말씀
이 도움말은 프랑수아 보포르가 검토했습니다.