ترويض WebRTC باستخدام PeerJS: إنشاء لعبة ويب بسيطة لأنشطة P2P
نشرت: 2022-03-11WebRTC هي تقنية تتيح الاتصال في الوقت الفعلي بين متصفحات الويب. إنه جديد نسبيًا ، ولا يزال تعريف API يعتبر مسودة. إلى جانب حقيقة أن WebRTC غير مدعوم من قبل جميع متصفحات الويب الرئيسية حتى الآن ، (ومن بين تلك التي تدعم ، لا يدعم بعضها كل ميزة من ميزات هذه التقنية) ، هذا يجعل من الصعب نسبيًا استخدام WebRTC لأي تطبيقات مهمة. . أو هكذا تعتقد!
منذ أن تم تقديمه لأول مرة بواسطة Google في مايو 2011 ، تم استخدام WebRTC في العديد من تطبيقات الويب الحديثة. نظرًا لكونها ميزة أساسية للعديد من متصفحات الويب الحديثة ، يمكن لتطبيقات الويب الاستفادة بسلاسة من هذه التقنية لتقديم تجربة مستخدم محسنة بعدة طرق. تطبيقات دفق الفيديو أو عقد المؤتمرات التي لا تتطلب مكونات إضافية متضخمة للمتصفح ، ويمكن أن تستفيد من شبكات نظير إلى نظير (P2P) (مع عدم نقل كل جزء من البيانات عبر بعض الخوادم) ليست سوى جزء من كل الأشياء المدهشة التي يمكن تحقيقه باستخدام WebRTC.
في هذه المقالة ، سوف نلقي نظرة على كيفية استخدام WebRTC لإنشاء لعبة ويب P2P بسيطة من Connect Four. للتغلب على مختلف الحواف الخشنة والاختلافات في التنفيذ لـ WebRTC ، سنستخدم مكتبة JavaScript مذهلة: PeerJS.
البيانات عبر WebRTC
قبل أن نبدأ ، من المهم أن نفهم أن WebRTC لا يتعلق فقط بنقل تدفقات الصوت والفيديو. كما يوفر دعمًا لقنوات بيانات P2P. تأتي هذه القنوات في شكلين: موثوقة وغير موثوقة. كما قد يتخيل المرء ، تضمن قنوات البيانات الموثوقة تسليم الرسائل وتسليمها بالترتيب ، بينما لا تقدم القنوات غير الموثوقة مثل هذه الضمانات.
علاوة على ذلك ، لا تتطلب قنوات بيانات WebRTC أي إعداد خاص للبنية التحتية ، بخلاف ما هو مطلوب بواسطة اتصال نظير WebRTC نموذجي: خادم إشارات لتنسيق الاتصال بين الأقران ، وخادم STUN لاكتشاف الهوية العامة للأقران ، وخادم TURN اختياريًا لتوجيه الرسائل بين الأقران إذا تعذر إنشاء اتصال مباشر بين الأقران (على سبيل المثال عندما يكون كلا الزملاء وراء NATs). إذا بدت هذه الاختصارات مألوفة ، فذلك لأن WebRTC تعيد استخدام التقنيات الحالية حيثما أمكن ذلك.
يفتح هذا الباب أمام المزيد من حالات استخدام WebRTC ، بما في ذلك على سبيل المثال لا الحصر الألعاب متعددة اللاعبين ، وتسليم المحتوى ، ومشاركة الملفات. مرة أخرى ، كل ذلك دون الحاجة إلى أي خادم وسيط وبالتالي بأوقات انتقال أقل.
في لعبة الويب البسيطة الخاصة بنا ، سنستخدم قناة بيانات بين متصفحي ويب لإيصال تحركات اللاعب ذهابًا وإيابًا.
قابل PeerJS
يأخذ PeerJS تنفيذ WebRTC في متصفحك ويلف حوله واجهة برمجة تطبيقات بسيطة ومتسقة وأنيقة. يقوم بسد الثغرات المختلفة في تنفيذ WebRTC للمتصفحات السابقة. على سبيل المثال ، في Chrome 30 أو الأقدم ، كانت قنوات البيانات غير الموثوق بها متاحة فقط. إذا تم تكوين PeerJS لاستخدام قنوات بيانات موثوقة ، فسيستخدم رقاقة لتلك المتصفحات القديمة. على الرغم من أن هذا لن يكون بنفس أداء التنفيذ المحلي للقنوات الموثوقة ، إلا أنه لا يزال يعمل.
مع PeerJS ، أصبح التعرف على الأقران أكثر بساطة. يتم التعرف على كل نظير باستخدام الهوية فقط. سلسلة يمكن للنظير أن يختارها بنفسه ، أو أن يقوم الخادم بتوليد واحدة. على الرغم من أن WebRTC يعد بالاتصال من نظير إلى نظير ، إلا أنك لا تزال بحاجة إلى خادم على أي حال للعمل كوسيط اتصال والتعامل مع الإشارات. يوفر PeerJS تطبيقًا مفتوح المصدر لخادم وسيط الاتصال PeerJS Server (مكتوب في Node.js) ، في حالة عدم رغبتك في استخدام الإصدار المستضاف على السحابة (وهو مجاني الآن ، ويأتي مع بعض القيود).
قم بتوصيل Four Goes P2P
الآن بعد أن أصبح لدينا مصدر ثقة للعمل مع WebRTC ، أي PeerJS ، فلنبدأ بإنشاء تطبيق Node.js / Express بسيط.
npm init npm install express --save npm install jade --save npm install peer --saveسنستخدم هذا فقط لاستضافة خادم PeerJS ، وتقديم صفحة وأصول أمامية. سنحتاج إلى تقديم صفحة واحدة فقط ، وسيحتوي هذا على قسمين: قائمة رئيسية عادية وشبكة 7 في 6 Connect Four.
خادم PeerJS
إن استضافة خادم PeerJS الخاص بنا أمر سهل حقًا. يحتوي المستودع الرسمي على GitHub على زر بنقرة واحدة لنشر مثيل PeerJS Server على Heroku.
في حالتنا ، نريد فقط إنشاء مثيل من ExpressPeerServer في تطبيق Node.js الخاص بنا ، وتقديمه على "/ peerjs":
var express = require('express') var app = express() // … Configure Express, and register necessary route handlers srv = app.listen(process.env.PORT) app.use('/peerjs', require('peer').ExpressPeerServer(srv, { debug: true }))عميل PeerJS
مع تشغيل خادم PeerJS ، ننتقل إلى جانب العميل. كما تمت مناقشته سابقًا ، يتعرف PeerJS على أقرانهم بمعرفات فريدة. يمكن إنشاء هذه المعرفات بواسطة PeerServer لكل نظير تلقائيًا ، أو يمكننا اختيار واحد لكل نظير أثناء إنشاء مثيل كائنات Peer .
var peer = new Peer(id, options)هنا ، يمكن حذف المعرف تمامًا إذا أردنا أن ينشئ الخادم واحدًا لنا. في حالتنا ، هذا ما نريد القيام به. سيضمن PeerServer أن المعرفات التي يقدمها فريدة. الوسيطة الثانية ، الخيارات ، عادة ما تكون كائنًا يحتوي على مفتاح (مفتاح API ، إذا كنت تستخدم PeerServer المستضاف على السحابة ، أو المضيف ، أو المنفذ ، أو المسار ، وما إلى ذلك في حال كنت تستضيف PeerServer بنفسك).
var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })من أجل إنشاء اتصال بين اثنين من زملاء PeerJS ، يجب أن يعرف أحد الأقران معرف النظير الآخر. من أجل تبسيط الأمور ، في تنفيذنا لـ Connect Four عبر WebRTC ، سنطلب من اللاعب الذي يبدأ اللعبة مشاركة معرف نظيره مع خصمه. مع معرفة معرف النظير للوجهة ، فإن الاتصال البسيط بـ peer.connect (destId) هو كل ما نحتاجه:
var conn = peer.connect(destId)يصدر كل من كائن Peer وكائن DataConnection الذي تم إرجاعه بواسطة peer.connect (destId) بعض الأحداث المفيدة حقًا التي تستحق الاستماع إليها. لأغراض هذا البرنامج التعليمي ، نحن مهتمون بشكل خاص بحدث "البيانات" لكائن DataConnection وأحداث "الخطأ" لكلا الكائنين.

لإرسال البيانات إلى الطرف الآخر من الاتصال ، ما عليك سوى استدعاء conn.send (data) :
conn.send('hello')على الرغم من المبالغة قليلاً في تلبية احتياجاتنا هنا ، إلا أن PeerJS ينقل البيانات بين الأقران بعد ترميزها بتنسيق BinaryPack. يتيح ذلك للأقران توصيل السلاسل والأرقام والمصفوفات والكائنات وحتى النقاط.
لتلقي البيانات الواردة ، ما عليك سوى الاستماع إلى حدث "البيانات" على conn :
conn.on('data', function(data) { // data === 'hello' })وهذا كل ما نحتاجه إلى حد كبير!
منطق اللعبة
يُظهر للاعب الأول ، الذي يبدأ اللعبة ، معرف نظيره كما تم إنشاؤه بواسطة PeerJS والذي يمكنه مشاركته مع خصمه. بمجرد انضمام الخصم إلى اللعبة باستخدام معرف نظير اللاعب الأول ، يُسمح للاعب الأول بالقيام بنقل.
لعبة Connect Four ، كونها لعبة ذات قواعد وآليات بسيطة ، لديها نوع واحد فقط من الحركة: يجب على كل لاعب ، بدوره ، اختيار عمود وإسقاط قرص فيه. هذا يعني أن كل ما يحتاجه النظير للتواصل هو رقم العمود الذي اختار فيه اللاعب الحالي وضع قرصه فيه. سننقل هذه المعلومات كمصفوفة مكونة من عنصرين: سلسلة "نقل" ورقم - 0- الفهرس القائم على العمود من اليسار.
في كل مرة ينقر فيها اللاعب على عمود:
if(!turn) { // it is not the current player's turn return } var i // i = chosen column index if(grid[i].length == 6) { // the column doesn't have any more space available return } // track player's move locally grid[i].push(peerId) // end current player's turn turn = false conn.send(['move', i])بعد إرسال بيانات الحركة هذه إلى الخصم ، نقوم بتحديث حالة اللعبة محليًا. يتضمن ذلك تحديد ما إذا كان اللاعب الحالي قد ربح ، أو ما إذا كانت اللعبة قد انتهت بالتعادل.
في الطرف المتلقي لبيانات النقل هذه:
if(turn) { // ignore incoming move data when it is the current player's turn return } var i = data[1] if(grid[i].length == 6) { // ignore incoming move data when it is invalid return } // track opponent's move locally grid[i].push(opponent.peerId) // activate current player's turn turn = trueوبطبيعة الحال ، نقوم بعد ذلك بتحديث حالة اللعبة محليًا ، وتحديد ما إذا كان الخصم قد ربح أو انتهت المباراة بالتعادل.
لاحظ كيف نحتاج إلى إجراء فحوصات سلامة البيانات الواردة. هذا مهم لأنه مع الألعاب المستندة إلى WebRTC ، ليس لدينا خادم وسيط ومنطق لعبة قائم على الخادم للتحقق من بيانات النقل.
للحفاظ على المقتطفات بسيطة ، تم حذف أسطر التعليمات البرمجية التي تعمل على تحديث واجهة المستخدم. يمكنك العثور على شفرة المصدر الكاملة لجافا سكريبت من جانب العميل هنا.
ربط كل شيء
لدمج كل ذلك ، نقوم بإنشاء صفحة بسيطة من قسمين. عند تحميل الصفحة ، يظهر القسم الذي يحتوي على القائمة الرئيسية ، ويتم إخفاء القسم الذي يحتوي على شبكة اللعبة.
section#menu div.animated.bounceIn div h1 Connect Four br div.no-support() div.alert.alert-warning p Unfortunately, your web browser does not <a href="http://iswebrtcreadyyet.com">support WebRTC</a> div a.btn.btn-primary.btn-lg(href='#start') Start | a.btn.btn-default.btn-lg(href='#join') Join section#game() div div h1 Connect Four br table.table.grid tbody for i in [0, 1, 2, 3, 4, 5] tr for j in [0, 1, 2, 3, 4, 5, 6] td div.slot br div.alert.alert-info pإن جعل عناصر DOM هذه تبدو جميلة خارج نطاق هذا البرنامج التعليمي. ومن ثم سوف نلجأ إلى رفيقنا الموثوق به Bootstrap ونقوم ببعض التصميم الخفيف فوقه.
عندما ينقر اللاعب الأول على زر "ابدأ" ، يتم الكشف عن شبكة اللعبة جنبًا إلى جنب مع معرف نظير اللاعب. يمكن للاعب بعد ذلك مشاركة معرف النظير هذا مع خصمه.
يمكن للاعب الثاني النقر ثم النقر فوق الزر "انضمام" ، وإدخال معرف نظير اللاعب الأول ، وبدء اللعبة.
تجربته
يمكنك تجربة هذا التطبيق النموذجي على https://arteegee.herokuapp.com.
أو يمكنك استنساخ المستودع من GitHub وتثبيت تبعيات NPM وتجربته محليًا:
git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm startبمجرد تشغيل الخادم ، يمكنك توجيه مستعرض الويب الخاص بك إلى http: // localhost: 5000 ، وبدء لعبة من علامة تبويب واحدة ، والانضمام من علامة تبويب أخرى (أو حتى متصفح ويب مختلف قادر على WebRTC) باستخدام معرف النظير.
يمكنك فتح وحدة تحكم مستعرض الويب الخاص بك للاطلاع على بعض معلومات التصحيح ، كما هو الحال في هذا المثال التطبيق ، تم تكوين عميل PeerJS لإجراء التسجيل المطول.
لكنها لا تعمل معي!
هناك سببان رئيسيان لعدم عمل هذه اللعبة على جهاز الكمبيوتر الخاص بك.
من المحتمل أنك تستخدم متصفح ويب لا يدعم واجهات برمجة تطبيقات WebRTC الضرورية حتى الآن. إذا كان الأمر كذلك ، فقد ترغب في تجربة متصفح مختلف - متصفح يدعم WebRTC وقنوات البيانات.
إذا كنت تستخدم متصفح ويب حديثًا يدعم WebRTC ، فهناك احتمال أن تكون وراء بعض البنية التحتية للشبكة التي لا يستطيع WebRTC اختراقها. من الناحية المثالية ، يمكن معالجة هذه المشكلة بسهولة باستخدام خادم TURN ، ولكن نظرًا لأن التطبيق النموذجي لا يستخدم واحدًا ، فلن يعمل عندما تكون أنت وخصمك وراء NATs.
خاتمة
WebRTC هي تقنية جديدة ، وتطبيقاتها بعيدة كل البعد عن النضج. غالبًا ما تسبب هذه بعض التحديات الفريدة للمطورين. ومع ذلك ، مع توفر مكتبات مثل PeerJS التي تلخص بدقة واجهات برمجة التطبيقات الأولية الخام ، أصبحت التكنولوجيا متاحة تمامًا بالفعل.
آمل أن يساعدك هذا البرنامج التعليمي الموجز لبناء لعبة قائمة على PeerJS في البدء باستخدام WebRTC وإنشاء بعض تطبيقات الويب المدهشة من نظير إلى نظير في الوقت الفعلي.
