שיחה עם הבקר של Stadia עם WebHID

הבקר המהבהב של Stadia פועל כמו גיימפאד רגיל, ולכן לא ניתן לגשת לכל הלחצנים שלו באמצעות Gamepad API. באמצעות WebHID, עכשיו אפשר לגשת ללחצנים החסרים.

מאז שפלטפורמת Stadia נסגרה, רבים חששו שהשלט רחוק יהפוך לחומרה חסרת תועלת באתר מטמנה. למרבה המזל, הצוות של Stadia החליט לפתוח את השלט רחוק של Stadia באמצעות קושחה מותאמת אישית. ניתן להפעיל אותה על השלט הרחוק ולעבור לדף מצב Bluetooth של Stadia. בעקבות זאת, השלט הרחוק של Stadia ייראה כגיימפאד סטנדרטי שאפשר להתחבר אליו באמצעות כבל USB או בחיבור אלחוטי דרך Bluetooth. דף ה-Bluetooth ב-Stadia מוצג ב-Project Fugu API ב-WebHID וב-WebUSB, אבל זה לא הנושא של המאמר. בפוסט הזה אני רוצה להסביר איך אפשר לדבר עם השלט רחוק של Stadia באמצעות WebHID.

שלט רחוק ל-Stadia בתור גיימפאד רגיל

אחרי ההבהוב, השלט הרחוק מופיע כגיימפאד רגיל במערכת ההפעלה. בצילום המסך הבא תוכלו לראות סידור של לחצנים וצירים בגיימפאד רגיל. כפי שמוגדר במפרט של Gamepad API, בגיימפאדים רגילים יש לחצנים מ-0 עד 16, כך שיש 17 לחצנים בסך הכול (לחצני החיצים (d-pad) נחשבים כארבעה לחצנים. אם תנסו את השלט רחוק של Stadia בהדגמה של בודק הגיימפאד, תשימו לב שהוא פועל כמו קסם.

סכימה של בקר משחקים רגיל עם תוויות של הצירים והלחצנים השונים.

עם זאת, אם סופרים את הלחצנים בבקר של Stadia, מופיעים 19. אם תנסו באופן שיטתי את האפליקציות האלה בבודק הגיימפאד, תבינו שהלחצנים Assistant והצילום לא פועלים. גם אם buttons המאפיין של הגיימפאד הוא פתוח, כי הבקר של Stadia מופיע בתור גיימפאד רגיל, רק לחצנים 0 עד 16 ממופים. עדיין אפשר להשתמש בלחצנים האחרים, אבל רוב המשחקים לא יצפו שהם קיימים.

WebHID מנסה להציל

הודות ל-WebHID API, אפשר לדבר אל הלחצנים החסרים 17 ו-18. ואם באמת תרצו, תוכלו לקבל נתונים על כל שאר הלחצנים והצירים שכבר זמינים דרך ממשק ה-API של Gamepad. השלב הראשון הוא להבין איך הבקר של Stadia מדווח למערכת ההפעלה. אחת מהדרכים לעשות זאת היא לפתוח את Chrome DevTools Console בכל דף אקראי, ולבקש מ-WebHID API רשימת מכשירים ללא סינון. לאחר מכן, בוחרים באופן ידני את השלט הרחוק של Stadia כדי להמשיך בבדיקה. אפשר לקבל רשימה של מכשירים ללא סינון על ידי העברה של מערך אפשרויות filters ריק.

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

בבורר, הרשומה לפני האחרונה נראית כמו שלט רחוק של Stadia.

בוחר המכשירים של WebHID API שמוצגים בו כמה מכשירים לא קשורים, והבקר של Stadia נמצא במיקום הלפני האחרון.

אחרי שבוחרים במכשיר 'שיפור ההכנסות מהבקר של Stadia', צריך לרשום במסוף את האובייקט HIDDevice שמתקבל. הפעולה הזו חושפת את productId של השלט הרחוק ל-Stadia (37888, כלומר 0x9400 בהקסדצימלי) ואת vendorId (6353, שהוא 0x18d1 בהקסדצימלי). אם תחפשו את vendorID בטבלת המזהים של ספק ה-USB הרשמית, תראו ש-6353 ממופה למה שציפיתם: Google Inc..

מסוף כלי הפיתוח ל-Chrome שבו מוצג הפלט של רישום האובייקט HIDDevice.

לחלופין, אפשר לנווט אל chrome://device-log/ בסרגל כתובות ה-URL, ללחוץ על הלחצן Clear, לחבר את הבקר של Stadia וללחוץ על רענון. הפעולה הזו מספקת את אותו המידע.

ממשק ניפוי הבאגים chrome://device-log שמוצג בו מידע על השלט הרחוק המחובר ל-Stadia.

חלופה אחרת היא באמצעות הכלי HID Explorer שמאפשר לכם לחקור פרטים נוספים לגבי מכשירי ממשק אנושי (HID) שמחוברים למחשב.

אפשר להשתמש בשני המזהים האלה, vendorId ו-productId, כדי לצמצם את מה שיוצג בבורר. עכשיו אפשר לסנן בצורה נכונה למכשיר ה-WebHID הנכון.

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

הרעש מכל המכשירים הלא קשורים נעלם, ורק השלט של Stadia מופיע.

בוחר המכשירים של WebHID API שמוצגים בו רק שלט רחוק ל-Stadia.

בשלב הבא, פותחים את HIDDevice על ידי קריאה לשיטה open().

await stadiaController.open();

רישום מחדש של HIDDevice, והדגל opened מוגדר ל-true.

מסוף כלי הפיתוח ל-Chrome שבו מוצג הפלט של רישום אובייקט HIDDevice לאחר פתיחתו.

כשהמכשיר פתוח, אפשר להאזין לאירועים נכנסים של inputreport על ידי צירוף של event listener.

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

כשלוחצים על לחצן Assistant בשלט הרחוק ומשחררים אותו, שני אירועים נרשמים במסוף. הם מוגדרים לאירועי "לחצן Assistant" ו "לחצן Assistant". מלבד timeStamp, לא ניתן להבחין במבט ראשון בשני האירועים.

מסוף כלי הפיתוח ל-Chrome שמוצגים בו אובייקטים מסוג HIDinputReportEvent שמתועדים.

המאפיין reportId בממשק HIDInputReportEvent מחזיר קידומת זיהוי בבייט אחד לדוח הזה, או 0 אם ממשק HID לא משתמש במזהי דוחות. במקרה הזה המספר הוא 3. ה-Secret נמצא במאפיין data, שמיוצג כ-DataView בגודל 10. DataView מספק ממשק ברמה נמוכה לקריאה ולכתיבה של סוגי מספרים מרובים בקובץ בינארי ArrayBuffer. הדרך להבין בצורה טובה יותר את הייצוג הזה היא ליצור Uint8Array מתוך ה-ArrayBuffer, כדי שתוכלו לראות את המספרים השלמים הנפרדים של 8 ביט שלא חתומים.

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

כשרושמים מחדש את נתוני האירועים בדוח הקלט, הדברים מתחילים להיראות הגיוניים יותר והלחצן "Assistant לא פעיל" ו "לחצן Assistant למעלה" מתחילים להיות ניתנים לפענוח. נראה שהמספר השלם הראשון (8 בשני האירועים) קשור ללחיצות על לחצנים, והמספר השלם השני (2 ו-0) קשור לשאלה אם לחצת על לחצן Assistant או לא.

מסוף כלי הפיתוח ל-Chrome שמוצגים בו אובייקטים של Uint8Array שמתועדים עבור כל HIDinputReportEvent.

לוחצים על הלחצן Capture במקום על הלחצן Assistant ורואים שהמספר השלם השני משתנה מ-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');
    }
  }
});

באמצעות הנדסה הפוכה כזו, אפשר להבין איך לדבר עם הבקר של Stadia באמצעות WebHID, בלחיצה על לחצן אחר לחצן ועל ציר אחר ציר. אחרי שמבינים את זה, השאר עובדים על מיפוי מספרים שלמים כמעט מכניים.

הדבר היחיד שחסר כרגע הוא חוויית החיבור החלקה ש-Gamepad API מספק. מטעמי אבטחה, תמיד תצטרכו לעבור את החוויה הראשונית של הבורר פעם אחת כדי לעבוד עם מכשיר WebHID, כמו שלט רחוק של Stadia, אבל בחיבורים עתידיים תוכלו להתחבר מחדש למכשירים מוכרים. כדי לעשות זאת, קוראים לשיטה getDevices().

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

הדגמה (דמו)

אפשר לראות את השלט הרחוק של Stadia שנמצא בשליטה משותפת של Gamepad API ושל WebHID API בהדגמה שיצרתי. חשוב לבדוק את קוד המקור, שמבוסס על קטעי הקוד במאמר הזה. כדי לפשט את העניינים, הצגת הלחצנים A, B, X ו-Y בלבד (שנשלטים על ידי Gamepad API) ולחצני Assistant ו-Capture (בשליטת WebHID API). מתחת לתמונת הבקר, אפשר לראות את נתוני ה-WebHID הגולמיים כדי להתנסות בכל הלחצנים והצירים בשלט.

אפליקציית ההדגמה בכתובת https://stadia-controller-webhid-gamepad.glitch.me/ שבה מוצגים הלחצנים A, B, X ו-Y שנשלטים על ידי Gamepad API, ולחצני Assistant והצילום שנשלטים על ידי WebHID API.

מסקנות

בזכות הקושחה החדשה, הבקר של Stadia זמין עכשיו כגיימפאד רגיל עם 17 לחצנים, שברוב המקרים יספיק לכם לשלוט בו במשחקי אינטרנט נפוצים. אם, מסיבה כלשהי, אתם צריכים נתונים מכל 19 הלחצנים בבקר, טכנולוגיית WebHID מאפשרת לכם לקבל גישה לדוחות קלט ברמה נמוכה שתוכלו לפענח אותם על ידי הנדסה הפוכה אחת אחרי השנייה. אם אחרי שתקרא את המאמר הזה תכתוב מנהל התקן WebHID מלא, אשמח לקשר את הפרויקט שלך לכאן. WebHIDing שמח!

אישורים

המאמר הזה נבדק על ידי פרנסואה בופורט.